mirror of
https://github.com/fastfetch-cli/fastfetch.git
synced 2026-11-04 05:54:17 +01:00
Compare commits
21 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| a5fb1ea061 | |||
| 9e58c70745 | |||
| f7c27253af | |||
| 6c4ae07971 | |||
| aeb22b4081 | |||
| 2c99d020ec | |||
| d697197a8b | |||
| d6c15e1cec | |||
| d4dd15763f | |||
| 7d5174a489 | |||
| 1ee988a698 | |||
| 2eea01aac3 | |||
| 44b32e9f84 | |||
| 06957877a8 | |||
| 2f68ce7f74 | |||
| 408646df1c | |||
| ad62e3d1f5 | |||
| 197d1c6e5d | |||
| bfe74b8437 | |||
| bbb92543b0 | |||
| 99785f54dd |
@@ -157,7 +157,7 @@ jobs:
|
|||||||
ls -l fastfetch
|
ls -l fastfetch
|
||||||
|
|
||||||
- name: upload artifacts
|
- name: upload artifacts
|
||||||
uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7.0.1
|
uses: actions/upload-artifact@cf430e030ddbb5b0abf93d22962f4752f3646cd9 # v7.0.2
|
||||||
with:
|
with:
|
||||||
name: fastfetch-android-${{ inputs.abi }}
|
name: fastfetch-android-${{ inputs.abi }}
|
||||||
path: ./fastfetch-*.*
|
path: ./fastfetch-*.*
|
||||||
|
|||||||
@@ -17,7 +17,7 @@ jobs:
|
|||||||
uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
|
uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
|
||||||
|
|
||||||
- name: download benchmark data
|
- name: download benchmark data
|
||||||
uses: actions/download-artifact@3e5f45b2cfb9172054b4087a40e8e0b5a5461e7c # v8.0.1
|
uses: actions/download-artifact@9000827ccba6bdab643e8b6fd33ac0654aef8333 # v8.0.2
|
||||||
with:
|
with:
|
||||||
pattern: benchmark-*
|
pattern: benchmark-*
|
||||||
path: benchmarks
|
path: benchmarks
|
||||||
|
|||||||
@@ -18,7 +18,7 @@ jobs:
|
|||||||
uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
|
uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
|
||||||
|
|
||||||
- name: Start VM
|
- name: Start VM
|
||||||
uses: cross-platform-actions/action@e0b9770014ba65d5e0815f15b74031c3a635f641 # v1.6.0
|
uses: cross-platform-actions/action@82668f1fbd89d9c99487604f822cbf6716f124d3 # v1.7.0
|
||||||
with:
|
with:
|
||||||
operating_system: dragonflybsd
|
operating_system: dragonflybsd
|
||||||
version: '6.4.2'
|
version: '6.4.2'
|
||||||
@@ -60,7 +60,7 @@ jobs:
|
|||||||
run: ctest --output-on-failure
|
run: ctest --output-on-failure
|
||||||
|
|
||||||
- name: upload artifacts
|
- name: upload artifacts
|
||||||
uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7.0.1
|
uses: actions/upload-artifact@cf430e030ddbb5b0abf93d22962f4752f3646cd9 # v7.0.2
|
||||||
with:
|
with:
|
||||||
name: fastfetch-dragonfly-amd64
|
name: fastfetch-dragonfly-amd64
|
||||||
path: ./fastfetch-*.*
|
path: ./fastfetch-*.*
|
||||||
|
|||||||
@@ -18,7 +18,7 @@ jobs:
|
|||||||
uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
|
uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
|
||||||
|
|
||||||
- name: Start VM
|
- name: Start VM
|
||||||
uses: cross-platform-actions/action@e0b9770014ba65d5e0815f15b74031c3a635f641 # v1.6.0
|
uses: cross-platform-actions/action@82668f1fbd89d9c99487604f822cbf6716f124d3 # v1.7.0
|
||||||
with:
|
with:
|
||||||
operating_system: freebsd
|
operating_system: freebsd
|
||||||
version: '15.1'
|
version: '15.1'
|
||||||
@@ -61,7 +61,7 @@ jobs:
|
|||||||
run: ctest --output-on-failure
|
run: ctest --output-on-failure
|
||||||
|
|
||||||
- name: upload artifacts
|
- name: upload artifacts
|
||||||
uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7.0.1
|
uses: actions/upload-artifact@cf430e030ddbb5b0abf93d22962f4752f3646cd9 # v7.0.2
|
||||||
with:
|
with:
|
||||||
name: fastfetch-freebsd-amd64
|
name: fastfetch-freebsd-amd64
|
||||||
path: ./fastfetch-*.*
|
path: ./fastfetch-*.*
|
||||||
|
|||||||
@@ -18,7 +18,7 @@ jobs:
|
|||||||
uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
|
uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
|
||||||
|
|
||||||
- name: Start VM
|
- name: Start VM
|
||||||
uses: cross-platform-actions/action@e0b9770014ba65d5e0815f15b74031c3a635f641 # v1.6.0
|
uses: cross-platform-actions/action@82668f1fbd89d9c99487604f822cbf6716f124d3 # v1.7.0
|
||||||
with:
|
with:
|
||||||
operating_system: haiku
|
operating_system: haiku
|
||||||
version: 'r1beta6'
|
version: 'r1beta6'
|
||||||
@@ -29,7 +29,7 @@ jobs:
|
|||||||
run: uname -a
|
run: uname -a
|
||||||
|
|
||||||
- name: Install dependencies
|
- name: Install dependencies
|
||||||
run: pkgman install -y git dbus_devel mesa_devel libelf_devel imagemagick_devel opencl_headers ocl_icd_devel vulkan_devel zlib_devel chafa_devel cmake llvm22_clang ninja pkgconfig python3.10 lua || pkgman install -y git dbus_devel mesa_devel libelf_devel imagemagick_devel opencl_headers ocl_icd_devel vulkan_devel zlib_devel chafa_devel cmake llvm22_clang ninja pkgconfig python3.10 lua
|
run: pkgman install -y git dbus_devel mesa_devel libelf_devel imagemagick_devel opencl_headers ocl_icd_devel vulkan_headers vulkan_loader_devel zlib_devel chafa_devel cmake llvm22_clang ninja pkgconfig python3.10 lua || pkgman install -y git dbus_devel mesa_devel libelf_devel imagemagick_devel opencl_headers ocl_icd_devel vulkan_headers vulkan_loader_devel zlib_devel chafa_devel cmake llvm22_clang ninja pkgconfig python3.10 lua
|
||||||
|
|
||||||
- name: CMake configuration
|
- name: CMake configuration
|
||||||
run: CC=clang CXX=clang++ cmake -GNinja -DSET_TWEAK=Off -DBUILD_TESTS=On -DENABLE_EMBEDDED_PCIIDS=On -DENABLE_EMBEDDED_AMDGPUIDS=On -DCMAKE_BUILD_TYPE=${{ vars.CMAKE_BUILD_TYPE || 'RelWithDebInfo' }} .
|
run: CC=clang CXX=clang++ cmake -GNinja -DSET_TWEAK=Off -DBUILD_TESTS=On -DENABLE_EMBEDDED_PCIIDS=On -DENABLE_EMBEDDED_AMDGPUIDS=On -DCMAKE_BUILD_TYPE=${{ vars.CMAKE_BUILD_TYPE || 'RelWithDebInfo' }} .
|
||||||
@@ -53,7 +53,7 @@ jobs:
|
|||||||
run: ctest --output-on-failure
|
run: ctest --output-on-failure
|
||||||
|
|
||||||
- name: upload artifacts
|
- name: upload artifacts
|
||||||
uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7.0.1
|
uses: actions/upload-artifact@cf430e030ddbb5b0abf93d22962f4752f3646cd9 # v7.0.2
|
||||||
with:
|
with:
|
||||||
name: fastfetch-haiku-amd64
|
name: fastfetch-haiku-amd64
|
||||||
path: ./fastfetch-*.*
|
path: ./fastfetch-*.*
|
||||||
|
|||||||
@@ -41,7 +41,7 @@ jobs:
|
|||||||
ctest --output-on-failure
|
ctest --output-on-failure
|
||||||
|
|
||||||
- name: upload artifacts
|
- name: upload artifacts
|
||||||
uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7.0.1
|
uses: actions/upload-artifact@cf430e030ddbb5b0abf93d22962f4752f3646cd9 # v7.0.2
|
||||||
with:
|
with:
|
||||||
name: fastfetch-linux-armv7l
|
name: fastfetch-linux-armv7l
|
||||||
path: ./fastfetch-*.*
|
path: ./fastfetch-*.*
|
||||||
|
|||||||
@@ -52,7 +52,7 @@ jobs:
|
|||||||
|
|
||||||
- name: Initialize CodeQL
|
- name: Initialize CodeQL
|
||||||
if: inputs.arch == 'amd64'
|
if: inputs.arch == 'amd64'
|
||||||
uses: github/codeql-action/init@2892aa5e19bbd11bc0cff5427e3b750a04d9e3c2 # v4.38.2
|
uses: github/codeql-action/init@9b3254085494b55a0f7c4c2b3d2d52cc712ce370 # v4.39.1
|
||||||
with:
|
with:
|
||||||
languages: c
|
languages: c
|
||||||
|
|
||||||
@@ -64,7 +64,7 @@ jobs:
|
|||||||
|
|
||||||
- name: perform CodeQL analysis
|
- name: perform CodeQL analysis
|
||||||
if: inputs.arch == 'amd64'
|
if: inputs.arch == 'amd64'
|
||||||
uses: github/codeql-action/analyze@2892aa5e19bbd11bc0cff5427e3b750a04d9e3c2 # v4.38.2
|
uses: github/codeql-action/analyze@9b3254085494b55a0f7c4c2b3d2d52cc712ce370 # v4.39.1
|
||||||
|
|
||||||
- name: list features
|
- name: list features
|
||||||
run: ./fastfetch --list-features
|
run: ./fastfetch --list-features
|
||||||
@@ -123,13 +123,13 @@ jobs:
|
|||||||
cpack -V
|
cpack -V
|
||||||
|
|
||||||
- name: upload artifacts
|
- name: upload artifacts
|
||||||
uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7.0.1
|
uses: actions/upload-artifact@cf430e030ddbb5b0abf93d22962f4752f3646cd9 # v7.0.2
|
||||||
with:
|
with:
|
||||||
name: fastfetch-linux-${{ inputs.arch }}
|
name: fastfetch-linux-${{ inputs.arch }}
|
||||||
path: ./fastfetch-*.*
|
path: ./fastfetch-*.*
|
||||||
|
|
||||||
- name: upload benchmark data
|
- name: upload benchmark data
|
||||||
uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7.0.1
|
uses: actions/upload-artifact@cf430e030ddbb5b0abf93d22962f4752f3646cd9 # v7.0.2
|
||||||
with:
|
with:
|
||||||
name: benchmark-linux-${{ inputs.arch }}
|
name: benchmark-linux-${{ inputs.arch }}
|
||||||
path: benchmark.json
|
path: benchmark.json
|
||||||
|
|||||||
@@ -67,7 +67,7 @@ jobs:
|
|||||||
run: ctest --output-on-failure
|
run: ctest --output-on-failure
|
||||||
|
|
||||||
- name: upload artifacts
|
- name: upload artifacts
|
||||||
uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7.0.1
|
uses: actions/upload-artifact@cf430e030ddbb5b0abf93d22962f4752f3646cd9 # v7.0.2
|
||||||
with:
|
with:
|
||||||
name: fastfetch-linux-i686
|
name: fastfetch-linux-i686
|
||||||
path: ./fastfetch-*.*
|
path: ./fastfetch-*.*
|
||||||
|
|||||||
@@ -54,7 +54,7 @@ jobs:
|
|||||||
'
|
'
|
||||||
|
|
||||||
- name: upload artifacts
|
- name: upload artifacts
|
||||||
uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7.0.1
|
uses: actions/upload-artifact@cf430e030ddbb5b0abf93d22962f4752f3646cd9 # v7.0.2
|
||||||
with:
|
with:
|
||||||
name: fastfetch-linux-loong64
|
name: fastfetch-linux-loong64
|
||||||
path: ./fastfetch-*.*
|
path: ./fastfetch-*.*
|
||||||
|
|||||||
@@ -42,7 +42,7 @@ jobs:
|
|||||||
ctest --output-on-failure
|
ctest --output-on-failure
|
||||||
|
|
||||||
- name: upload artifacts
|
- name: upload artifacts
|
||||||
uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7.0.1
|
uses: actions/upload-artifact@cf430e030ddbb5b0abf93d22962f4752f3646cd9 # v7.0.2
|
||||||
with:
|
with:
|
||||||
name: fastfetch-linux-${{ inputs.arch }}
|
name: fastfetch-linux-${{ inputs.arch }}
|
||||||
path: ./fastfetch-*.*
|
path: ./fastfetch-*.*
|
||||||
|
|||||||
@@ -74,13 +74,13 @@ jobs:
|
|||||||
run: ctest --output-on-failure
|
run: ctest --output-on-failure
|
||||||
|
|
||||||
- name: upload artifacts
|
- name: upload artifacts
|
||||||
uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7.0.1
|
uses: actions/upload-artifact@cf430e030ddbb5b0abf93d22962f4752f3646cd9 # v7.0.2
|
||||||
with:
|
with:
|
||||||
name: fastfetch-macos-universal
|
name: fastfetch-macos-universal
|
||||||
path: ./fastfetch-*.*
|
path: ./fastfetch-*.*
|
||||||
|
|
||||||
- name: upload benchmark data
|
- name: upload benchmark data
|
||||||
uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7.0.1
|
uses: actions/upload-artifact@cf430e030ddbb5b0abf93d22962f4752f3646cd9 # v7.0.2
|
||||||
with:
|
with:
|
||||||
name: benchmark-macos-universal
|
name: benchmark-macos-universal
|
||||||
path: benchmark.json
|
path: benchmark.json
|
||||||
|
|||||||
@@ -11,7 +11,7 @@ env:
|
|||||||
|
|
||||||
jobs:
|
jobs:
|
||||||
build:
|
build:
|
||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-24.04
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
|
- uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
|
||||||
|
|
||||||
@@ -44,7 +44,7 @@ jobs:
|
|||||||
shell: alpine.sh {0}
|
shell: alpine.sh {0}
|
||||||
|
|
||||||
- name: upload artifacts
|
- name: upload artifacts
|
||||||
uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7.0.1
|
uses: actions/upload-artifact@cf430e030ddbb5b0abf93d22962f4752f3646cd9 # v7.0.2
|
||||||
with:
|
with:
|
||||||
name: fastfetch-musl-amd64
|
name: fastfetch-musl-amd64
|
||||||
path: ./fastfetch-*.*
|
path: ./fastfetch-*.*
|
||||||
|
|||||||
@@ -18,7 +18,7 @@ jobs:
|
|||||||
uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
|
uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
|
||||||
|
|
||||||
- name: Start VM
|
- name: Start VM
|
||||||
uses: cross-platform-actions/action@e0b9770014ba65d5e0815f15b74031c3a635f641 # v1.6.0
|
uses: cross-platform-actions/action@82668f1fbd89d9c99487604f822cbf6716f124d3 # v1.7.0
|
||||||
with:
|
with:
|
||||||
operating_system: netbsd
|
operating_system: netbsd
|
||||||
version: '10.1'
|
version: '10.1'
|
||||||
@@ -59,7 +59,7 @@ jobs:
|
|||||||
run: ctest --output-on-failure
|
run: ctest --output-on-failure
|
||||||
|
|
||||||
- name: upload artifacts
|
- name: upload artifacts
|
||||||
uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7.0.1
|
uses: actions/upload-artifact@cf430e030ddbb5b0abf93d22962f4752f3646cd9 # v7.0.2
|
||||||
with:
|
with:
|
||||||
name: fastfetch-netbsd-amd64
|
name: fastfetch-netbsd-amd64
|
||||||
path: ./fastfetch-*.*
|
path: ./fastfetch-*.*
|
||||||
|
|||||||
@@ -18,7 +18,7 @@ jobs:
|
|||||||
uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
|
uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
|
||||||
|
|
||||||
- name: Start VM
|
- name: Start VM
|
||||||
uses: cross-platform-actions/action@e0b9770014ba65d5e0815f15b74031c3a635f641 # v1.6.0
|
uses: cross-platform-actions/action@82668f1fbd89d9c99487604f822cbf6716f124d3 # v1.7.0
|
||||||
with:
|
with:
|
||||||
operating_system: omnios
|
operating_system: omnios
|
||||||
version: 'r151058'
|
version: 'r151058'
|
||||||
@@ -64,7 +64,7 @@ jobs:
|
|||||||
cpack -V
|
cpack -V
|
||||||
|
|
||||||
- name: upload artifacts
|
- name: upload artifacts
|
||||||
uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7.0.1
|
uses: actions/upload-artifact@cf430e030ddbb5b0abf93d22962f4752f3646cd9 # v7.0.2
|
||||||
with:
|
with:
|
||||||
name: fastfetch-omnios-amd64
|
name: fastfetch-omnios-amd64
|
||||||
path: ./fastfetch-*.*
|
path: ./fastfetch-*.*
|
||||||
|
|||||||
@@ -18,7 +18,7 @@ jobs:
|
|||||||
uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
|
uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
|
||||||
|
|
||||||
- name: Start VM
|
- name: Start VM
|
||||||
uses: cross-platform-actions/action@e0b9770014ba65d5e0815f15b74031c3a635f641 # v1.6.0
|
uses: cross-platform-actions/action@82668f1fbd89d9c99487604f822cbf6716f124d3 # v1.7.0
|
||||||
with:
|
with:
|
||||||
operating_system: openbsd
|
operating_system: openbsd
|
||||||
version: '7.9'
|
version: '7.9'
|
||||||
@@ -59,7 +59,7 @@ jobs:
|
|||||||
run: ctest --output-on-failure
|
run: ctest --output-on-failure
|
||||||
|
|
||||||
- name: upload artifacts
|
- name: upload artifacts
|
||||||
uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7.0.1
|
uses: actions/upload-artifact@cf430e030ddbb5b0abf93d22962f4752f3646cd9 # v7.0.2
|
||||||
with:
|
with:
|
||||||
name: fastfetch-openbsd-amd64
|
name: fastfetch-openbsd-amd64
|
||||||
path: ./fastfetch-*.*
|
path: ./fastfetch-*.*
|
||||||
|
|||||||
@@ -22,7 +22,7 @@ jobs:
|
|||||||
|
|
||||||
- name: download artifacts
|
- name: download artifacts
|
||||||
if: inputs.ffversion != steps.get_version_release.outputs.release
|
if: inputs.ffversion != steps.get_version_release.outputs.release
|
||||||
uses: actions/download-artifact@3e5f45b2cfb9172054b4087a40e8e0b5a5461e7c # v8.0.1
|
uses: actions/download-artifact@9000827ccba6bdab643e8b6fd33ac0654aef8333 # v8.0.2
|
||||||
|
|
||||||
- name: create release
|
- name: create release
|
||||||
if: inputs.ffversion != steps.get_version_release.outputs.release
|
if: inputs.ffversion != steps.get_version_release.outputs.release
|
||||||
|
|||||||
@@ -17,7 +17,7 @@ jobs:
|
|||||||
uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
|
uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
|
||||||
|
|
||||||
- name: run VM
|
- name: run VM
|
||||||
uses: vmactions/solaris-vm@87d3c436511cef802cd1637f86f73b1a97715c8c # v1.4.1
|
uses: vmactions/solaris-vm@1c166978f5a2c1f87f1c9d4d8dd85ae3d1eff6f7 # v1.4.2
|
||||||
with:
|
with:
|
||||||
usesh: true
|
usesh: true
|
||||||
envs: 'CMAKE_BUILD_TYPE'
|
envs: 'CMAKE_BUILD_TYPE'
|
||||||
@@ -40,7 +40,7 @@ jobs:
|
|||||||
cpack -V
|
cpack -V
|
||||||
|
|
||||||
- name: upload artifacts
|
- name: upload artifacts
|
||||||
uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7.0.1
|
uses: actions/upload-artifact@cf430e030ddbb5b0abf93d22962f4752f3646cd9 # v7.0.2
|
||||||
with:
|
with:
|
||||||
name: fastfetch-solaris-amd64
|
name: fastfetch-solaris-amd64
|
||||||
path: ./fastfetch-*.*
|
path: ./fastfetch-*.*
|
||||||
|
|||||||
@@ -106,7 +106,7 @@ jobs:
|
|||||||
- if: github.event_name == 'push' && github.repository == 'fastfetch-cli/fastfetch'
|
- if: github.event_name == 'push' && github.repository == 'fastfetch-cli/fastfetch'
|
||||||
id: upload-unsigned-artifact
|
id: upload-unsigned-artifact
|
||||||
name: upload artifacts for signing
|
name: upload artifacts for signing
|
||||||
uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7.0.1
|
uses: actions/upload-artifact@cf430e030ddbb5b0abf93d22962f4752f3646cd9 # v7.0.2
|
||||||
with:
|
with:
|
||||||
name: fastfetch-windows-${{ inputs.arch }}
|
name: fastfetch-windows-${{ inputs.arch }}
|
||||||
path: |
|
path: |
|
||||||
@@ -134,14 +134,14 @@ jobs:
|
|||||||
run: 7z a -t7z -mx9 -bd -y fastfetch-windows-${{ inputs.arch }}.7z LICENSE *.dll fastfetch.exe flashfetch.exe presets
|
run: 7z a -t7z -mx9 -bd -y fastfetch-windows-${{ inputs.arch }}.7z LICENSE *.dll fastfetch.exe flashfetch.exe presets
|
||||||
|
|
||||||
- name: upload true artifacts
|
- name: upload true artifacts
|
||||||
uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7.0.1
|
uses: actions/upload-artifact@cf430e030ddbb5b0abf93d22962f4752f3646cd9 # v7.0.2
|
||||||
with:
|
with:
|
||||||
name: fastfetch-windows-${{ inputs.arch }}
|
name: fastfetch-windows-${{ inputs.arch }}
|
||||||
path: ./fastfetch-windows-${{ inputs.arch }}.*
|
path: ./fastfetch-windows-${{ inputs.arch }}.*
|
||||||
overwrite: true
|
overwrite: true
|
||||||
|
|
||||||
- name: upload benchmark data
|
- name: upload benchmark data
|
||||||
uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7.0.1
|
uses: actions/upload-artifact@cf430e030ddbb5b0abf93d22962f4752f3646cd9 # v7.0.2
|
||||||
with:
|
with:
|
||||||
name: benchmark-windows-${{ inputs.arch }}
|
name: benchmark-windows-${{ inputs.arch }}
|
||||||
path: benchmark.json
|
path: benchmark.json
|
||||||
|
|||||||
@@ -1,3 +1,25 @@
|
|||||||
|
# Unreleased
|
||||||
|
|
||||||
|
Features:
|
||||||
|
* Added display detection through the `org.gnome.Mutter.DisplayConfig` D-Bus interface on GNOME and Cinnamon Wayland sessions, which reports the primary monitor and the raw EDID directly instead of matching the `wl_output` name against the DRM connectors. A D-Bus enabled build is required. (Display, Linux)
|
||||||
|
|
||||||
|
Bugfixes:
|
||||||
|
* Fixed shell detection when launched via mise. (#2686, Shell)
|
||||||
|
* Rejected an empty `separator.string` in the JSON config with an error message. (#2689, Separator)
|
||||||
|
* Fixed fastfetch blocking forever when the terminal answered a terminal query only partially, for example with just one of the two OSC 10/11 color replies. (Logo / Terminal / TerminalTheme, Unix / Windows)
|
||||||
|
* Fixed a `$` ending a logo line or the logo itself being parsed as the start of a color code instead of a plain dollar sign. (#2666, Logo)
|
||||||
|
* Fixed AMD GPU name detection in amdgpu.ids when PCI device ID or revision ID has leading zeros. (GPU, Linux)
|
||||||
|
* Improved uptime detection on SunOS via kstat. (#2674, Uptime, SunOS)
|
||||||
|
* Fixed substring formatting returning the remaining string when a negative end index is before the start of the string. (General)
|
||||||
|
* Fixed a skipped `{?}` / `{/}` conditional in a format string ending at the `{?}` / `{/}` of a conditional nested in it. (General)
|
||||||
|
* Fixed numbers in format strings that don't fit in 32 bits wrapping around to a valid argument index, constant index, substring index or length, e.g. `{4294967297}` being read as `{1}`, and a signed integer overflow for `{1:-2147483648}`. (General)
|
||||||
|
* Fixed substring formatting printing the placeholder as is, instead of nothing, when the start index is outside of the string and an end index is given. (General)
|
||||||
|
* Fixed the CPU micro-architecture level (`march`) being under-reported on Windows on ARM when `IsProcessorFeaturePresent` does not expose a feature the CPU implements. (CPU, Windows)
|
||||||
|
* It is now decoded from the AArch64 ID registers in the registry, matching the Linux detection.
|
||||||
|
|
||||||
|
Logos:
|
||||||
|
* Added Tiny Core Linux (#2655)
|
||||||
|
|
||||||
# 2.70.0
|
# 2.70.0
|
||||||
|
|
||||||
Changes:
|
Changes:
|
||||||
@@ -42,6 +64,7 @@ Bugfixes:
|
|||||||
* On Windows 10 1809 (build 17763) and later, the whole volume's capacity and free space are now reported by default. Set `disk.useAvailable` to `true` to keep reporting the space that is available to the current user, quota included.
|
* On Windows 10 1809 (build 17763) and later, the whole volume's capacity and free space are now reported by default. Set `disk.useAvailable` to `true` to keep reporting the space that is available to the current user, quota included.
|
||||||
|
|
||||||
Logos:
|
Logos:
|
||||||
|
* Added ArmadaOS logo (#2641)
|
||||||
* Added filled Artix logo (#2631)
|
* Added filled Artix logo (#2631)
|
||||||
* Renamed PostmarketOS to nura (#2626)
|
* Renamed PostmarketOS to nura (#2626)
|
||||||
|
|
||||||
|
|||||||
@@ -623,6 +623,7 @@ if(LINUX)
|
|||||||
src/detection/displayserver/linux/common.c
|
src/detection/displayserver/linux/common.c
|
||||||
src/detection/displayserver/linux/drm.c
|
src/detection/displayserver/linux/drm.c
|
||||||
src/detection/displayserver/linux/wayland/wayland.c
|
src/detection/displayserver/linux/wayland/wayland.c
|
||||||
|
src/detection/displayserver/linux/wayland/mutter.c
|
||||||
src/detection/displayserver/linux/wayland/global-output.c
|
src/detection/displayserver/linux/wayland/global-output.c
|
||||||
src/detection/displayserver/linux/wayland/wl-output-protocol.c
|
src/detection/displayserver/linux/wayland/wl-output-protocol.c
|
||||||
src/detection/displayserver/linux/wayland/wp-color-management-v1-protocol.c
|
src/detection/displayserver/linux/wayland/wp-color-management-v1-protocol.c
|
||||||
@@ -712,6 +713,7 @@ elseif(ANDROID)
|
|||||||
src/detection/diskio/diskio_linux.c
|
src/detection/diskio/diskio_linux.c
|
||||||
src/detection/displayserver/displayserver_android.c
|
src/detection/displayserver/displayserver_android.c
|
||||||
src/detection/displayserver/linux/wayland/wayland.c
|
src/detection/displayserver/linux/wayland/wayland.c
|
||||||
|
src/detection/displayserver/linux/wayland/mutter.c
|
||||||
src/detection/displayserver/linux/wayland/global-output.c
|
src/detection/displayserver/linux/wayland/global-output.c
|
||||||
src/detection/displayserver/linux/wayland/kde-output.c
|
src/detection/displayserver/linux/wayland/kde-output.c
|
||||||
src/detection/displayserver/linux/wayland/wl-output-protocol.c
|
src/detection/displayserver/linux/wayland/wl-output-protocol.c
|
||||||
@@ -800,6 +802,7 @@ elseif(FreeBSD)
|
|||||||
src/detection/displayserver/linux/common.c
|
src/detection/displayserver/linux/common.c
|
||||||
src/detection/displayserver/linux/drm.c
|
src/detection/displayserver/linux/drm.c
|
||||||
src/detection/displayserver/linux/wayland/wayland.c
|
src/detection/displayserver/linux/wayland/wayland.c
|
||||||
|
src/detection/displayserver/linux/wayland/mutter.c
|
||||||
src/detection/displayserver/linux/wayland/global-output.c
|
src/detection/displayserver/linux/wayland/global-output.c
|
||||||
src/detection/displayserver/linux/wayland/kde-output.c
|
src/detection/displayserver/linux/wayland/kde-output.c
|
||||||
src/detection/displayserver/linux/wayland/wl-output-protocol.c
|
src/detection/displayserver/linux/wayland/wl-output-protocol.c
|
||||||
@@ -898,6 +901,7 @@ elseif(NetBSD)
|
|||||||
src/detection/displayserver/linux/common.c
|
src/detection/displayserver/linux/common.c
|
||||||
src/detection/displayserver/linux/drm.c
|
src/detection/displayserver/linux/drm.c
|
||||||
src/detection/displayserver/linux/wayland/wayland.c
|
src/detection/displayserver/linux/wayland/wayland.c
|
||||||
|
src/detection/displayserver/linux/wayland/mutter.c
|
||||||
src/detection/displayserver/linux/wayland/global-output.c
|
src/detection/displayserver/linux/wayland/global-output.c
|
||||||
src/detection/displayserver/linux/wayland/kde-output.c
|
src/detection/displayserver/linux/wayland/kde-output.c
|
||||||
src/detection/displayserver/linux/wayland/wl-output-protocol.c
|
src/detection/displayserver/linux/wayland/wl-output-protocol.c
|
||||||
@@ -983,6 +987,7 @@ elseif(OpenBSD)
|
|||||||
src/detection/displayserver/linux/common.c
|
src/detection/displayserver/linux/common.c
|
||||||
src/detection/displayserver/linux/drm.c
|
src/detection/displayserver/linux/drm.c
|
||||||
src/detection/displayserver/linux/wayland/wayland.c
|
src/detection/displayserver/linux/wayland/wayland.c
|
||||||
|
src/detection/displayserver/linux/wayland/mutter.c
|
||||||
src/detection/displayserver/linux/wayland/global-output.c
|
src/detection/displayserver/linux/wayland/global-output.c
|
||||||
src/detection/displayserver/linux/wayland/kde-output.c
|
src/detection/displayserver/linux/wayland/kde-output.c
|
||||||
src/detection/displayserver/linux/wayland/wl-output-protocol.c
|
src/detection/displayserver/linux/wayland/wl-output-protocol.c
|
||||||
@@ -1243,6 +1248,7 @@ elseif(SunOS)
|
|||||||
src/detection/displayserver/linux/common.c
|
src/detection/displayserver/linux/common.c
|
||||||
src/detection/displayserver/linux/drm.c
|
src/detection/displayserver/linux/drm.c
|
||||||
src/detection/displayserver/linux/wayland/wayland.c
|
src/detection/displayserver/linux/wayland/wayland.c
|
||||||
|
src/detection/displayserver/linux/wayland/mutter.c
|
||||||
src/detection/displayserver/linux/wayland/global-output.c
|
src/detection/displayserver/linux/wayland/global-output.c
|
||||||
src/detection/displayserver/linux/wayland/kde-output.c
|
src/detection/displayserver/linux/wayland/kde-output.c
|
||||||
src/detection/displayserver/linux/wayland/wl-output-protocol.c
|
src/detection/displayserver/linux/wayland/wl-output-protocol.c
|
||||||
@@ -1402,6 +1408,7 @@ elseif(GNU)
|
|||||||
src/detection/diskio/diskio_nosupport.c
|
src/detection/diskio/diskio_nosupport.c
|
||||||
src/detection/displayserver/linux/displayserver_linux.c
|
src/detection/displayserver/linux/displayserver_linux.c
|
||||||
src/detection/displayserver/linux/wayland/wayland.c
|
src/detection/displayserver/linux/wayland/wayland.c
|
||||||
|
src/detection/displayserver/linux/wayland/mutter.c
|
||||||
src/detection/displayserver/linux/common.c
|
src/detection/displayserver/linux/common.c
|
||||||
src/detection/displayserver/linux/drm.c
|
src/detection/displayserver/linux/drm.c
|
||||||
src/detection/displayserver/linux/wmde.c
|
src/detection/displayserver/linux/wmde.c
|
||||||
|
|||||||
@@ -4142,7 +4142,8 @@
|
|||||||
"string": {
|
"string": {
|
||||||
"description": "String to print for the separator line",
|
"description": "String to print for the separator line",
|
||||||
"type": "string",
|
"type": "string",
|
||||||
"default": "-"
|
"default": "-",
|
||||||
|
"minLength": 1
|
||||||
},
|
},
|
||||||
"outputColor": {
|
"outputColor": {
|
||||||
"description": "Color of the separator line",
|
"description": "Color of the separator line",
|
||||||
|
|||||||
+72
-13
@@ -63,6 +63,13 @@ void ffFormatAppendFormatArg(FFstrbuf* buffer, const FFformatarg* formatarg) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// strtol() to an int32_t. Values that don't fit are clamped instead of wrapping around,
|
||||||
|
// so that e.g. 4294967297 can't turn into 1.
|
||||||
|
static int32_t strToInt32(const char* str, char** pEnd) {
|
||||||
|
long long value = strtoll(str, pEnd, 10);
|
||||||
|
return value > INT32_MAX ? INT32_MAX : value < INT32_MIN ? INT32_MIN : (int32_t) value;
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @brief parses a string to a uint32_t
|
* @brief parses a string to a uint32_t
|
||||||
*
|
*
|
||||||
@@ -79,14 +86,14 @@ static uint32_t getArgumentIndex(const char* placeholderValue, uint32_t numArgs,
|
|||||||
|
|
||||||
if (firstChar >= '0' && firstChar <= '9') {
|
if (firstChar >= '0' && firstChar <= '9') {
|
||||||
char* pEnd = nullptr;
|
char* pEnd = nullptr;
|
||||||
uint32_t result = (uint32_t) strtoul(placeholderValue, &pEnd, 10);
|
unsigned long result = strtoul(placeholderValue, &pEnd, 10);
|
||||||
if (result > numArgs) {
|
if (result > numArgs) {
|
||||||
return UINT32_MAX;
|
return UINT32_MAX;
|
||||||
}
|
}
|
||||||
if (*pEnd != '\0') {
|
if (*pEnd != '\0') {
|
||||||
return UINT32_MAX;
|
return UINT32_MAX;
|
||||||
}
|
}
|
||||||
return result;
|
return (uint32_t) result;
|
||||||
} else if (ffCharIsEnglishAlphabet(firstChar)) {
|
} else if (ffCharIsEnglishAlphabet(firstChar)) {
|
||||||
for (uint32_t i = 0; i < numArgs; ++i) {
|
for (uint32_t i = 0; i < numArgs; ++i) {
|
||||||
const FFformatarg* arg = &arguments[i];
|
const FFformatarg* arg = &arguments[i];
|
||||||
@@ -343,6 +350,51 @@ static bool skipAnsiEscape(FFstrbuf* in, FFstrbuf* out, FFstrbuf* trailingEscape
|
|||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Returns the index of the `}` of the `{<marker>}` that closes the conditional we are in, or the
|
||||||
|
// length of the format string if there is none. `i` is the index of the `}` that ends the opening
|
||||||
|
// placeholder. The part to skip may hold conditionals of its own, which have to be passed over.
|
||||||
|
// Like in `parseFormatString`, only a conditional with a valid argument opens a block.
|
||||||
|
static uint32_t findConditionalEnd(const FFstrbuf* formatstr, uint32_t i, char marker, uint32_t numArgs, const FFformatarg* arguments) {
|
||||||
|
FF_STRBUF_AUTO_DESTROY placeholderValue = ffStrbufCreate();
|
||||||
|
uint32_t depth = 0;
|
||||||
|
|
||||||
|
for (++i; i < formatstr->length; ++i) {
|
||||||
|
if (formatstr->chars[i] != '{') {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
++i;
|
||||||
|
if (i >= formatstr->length) {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
// double {{ evaluates to a single { and doesn't start a placeholder
|
||||||
|
if (formatstr->chars[i] == '{') {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
uint32_t iEnd = ffStrbufNextIndexC(formatstr, i, '}');
|
||||||
|
if (formatstr->chars[i] == marker) {
|
||||||
|
if (iEnd - i == 1) {
|
||||||
|
if (depth == 0) {
|
||||||
|
return iEnd;
|
||||||
|
}
|
||||||
|
--depth;
|
||||||
|
} else {
|
||||||
|
ffStrbufClear(&placeholderValue);
|
||||||
|
ffStrbufAppendNS(&placeholderValue, iEnd - i - 1, &formatstr->chars[i + 1]);
|
||||||
|
uint32_t index = getArgumentIndex(placeholderValue.chars, numArgs, arguments);
|
||||||
|
if (index >= 1 && index <= numArgs) {
|
||||||
|
++depth;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
i = iEnd;
|
||||||
|
}
|
||||||
|
|
||||||
|
return formatstr->length;
|
||||||
|
}
|
||||||
|
|
||||||
static bool parseFormatString(FFstrbuf* buffer, const FFstrbuf* formatstr, uint32_t numArgs, const FFformatarg* arguments) {
|
static bool parseFormatString(FFstrbuf* buffer, const FFstrbuf* formatstr, uint32_t numArgs, const FFformatarg* arguments) {
|
||||||
uint32_t argCounter = 0;
|
uint32_t argCounter = 0;
|
||||||
|
|
||||||
@@ -440,7 +492,7 @@ static bool parseFormatString(FFstrbuf* buffer, const FFstrbuf* formatstr, uint3
|
|||||||
}
|
}
|
||||||
|
|
||||||
// fastforward to the end of the if without printing the in between
|
// fastforward to the end of the if without printing the in between
|
||||||
i = ffStrbufNextIndexS(formatstr, i, "{?}") + 2; // 2 is the length of "{?}" - 1 because the loop will increment it again directly after continue
|
i = findConditionalEnd(formatstr, i, '?', numArgs, arguments);
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -463,7 +515,7 @@ static bool parseFormatString(FFstrbuf* buffer, const FFstrbuf* formatstr, uint3
|
|||||||
}
|
}
|
||||||
|
|
||||||
// fastforward to the end of the if without printing the in between
|
// fastforward to the end of the if without printing the in between
|
||||||
i = ffStrbufNextIndexS(formatstr, i, "{/}") + 2; // 2 is the length of "{/}" - 1 because the loop will increment it again directly after continue
|
i = findConditionalEnd(formatstr, i, '/', numArgs, arguments);
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -480,7 +532,7 @@ static bool parseFormatString(FFstrbuf* buffer, const FFstrbuf* formatstr, uint3
|
|||||||
// test for constant or env var, if so evaluate it
|
// test for constant or env var, if so evaluate it
|
||||||
if (firstChar == '$') {
|
if (firstChar == '$') {
|
||||||
char* pend = nullptr;
|
char* pend = nullptr;
|
||||||
int32_t indexSigned = (int32_t) strtol(placeholderValue.chars + 1, &pend, 10);
|
int32_t indexSigned = strToInt32(placeholderValue.chars + 1, &pend);
|
||||||
if (pend == placeholderValue.chars + 1) {
|
if (pend == placeholderValue.chars + 1) {
|
||||||
// treat placeholder as an environment variable
|
// treat placeholder as an environment variable
|
||||||
char* envValue = getenv(placeholderValue.chars + 1);
|
char* envValue = getenv(placeholderValue.chars + 1);
|
||||||
@@ -539,20 +591,27 @@ static bool parseFormatString(FFstrbuf* buffer, const FFstrbuf* formatstr, uint3
|
|||||||
skipAnsiEscape(&tempString, buffer, &trailingEscape);
|
skipAnsiEscape(&tempString, buffer, &trailingEscape);
|
||||||
|
|
||||||
char* pEnd = nullptr;
|
char* pEnd = nullptr;
|
||||||
int32_t start = (int32_t) strtol(pSep + 1, &pEnd, 10);
|
int32_t start = strToInt32(pSep + 1, &pEnd);
|
||||||
|
bool hasEnd = *pEnd == ',';
|
||||||
|
int32_t end = 0;
|
||||||
|
if (hasEnd) {
|
||||||
|
end = strToInt32(pEnd + 1, &pEnd);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Check the whole range before using it, so that a valid range that lies
|
||||||
|
// outside of the string gives an empty result instead of an invalid placeholder
|
||||||
|
if (!*pEnd) {
|
||||||
if (start < 0) {
|
if (start < 0) {
|
||||||
start = (int32_t) tempString.length + start;
|
start = (int32_t) tempString.length + start;
|
||||||
}
|
}
|
||||||
if (start >= 0 && (uint32_t) start < tempString.length) {
|
if (start >= 0 && (uint32_t) start < tempString.length) {
|
||||||
if (*pEnd == '\0') {
|
if (!hasEnd) {
|
||||||
ffStrbufAppendNS(buffer, tempString.length - (uint32_t) start, &tempString.chars[start]);
|
ffStrbufAppendNS(buffer, tempString.length - (uint32_t) start, &tempString.chars[start]);
|
||||||
} else if (*pEnd == ',') {
|
} else {
|
||||||
int32_t end = (int32_t) strtol(pEnd + 1, &pEnd, 10);
|
|
||||||
if (!*pEnd) {
|
|
||||||
if (end < 0) {
|
if (end < 0) {
|
||||||
end = (int32_t) tempString.length + end;
|
end = (int32_t) tempString.length + end;
|
||||||
}
|
}
|
||||||
if ((uint32_t) end > tempString.length) {
|
if (end >= 0 && (uint32_t) end > tempString.length) {
|
||||||
end = (int32_t) tempString.length;
|
end = (int32_t) tempString.length;
|
||||||
}
|
}
|
||||||
if (end > start) {
|
if (end > start) {
|
||||||
@@ -573,7 +632,7 @@ static bool parseFormatString(FFstrbuf* buffer, const FFstrbuf* formatstr, uint3
|
|||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
char* pEnd = nullptr;
|
char* pEnd = nullptr;
|
||||||
int32_t truncLength = (int32_t) strtol(pSep + 1, &pEnd, 10);
|
int32_t truncLength = strToInt32(pSep + 1, &pEnd);
|
||||||
if (*pEnd != '\0') {
|
if (*pEnd != '\0') {
|
||||||
*pSep = cSep;
|
*pSep = cSep;
|
||||||
appendInvalidPlaceholder(buffer, "{", &placeholderValue, i, formatstr->length);
|
appendInvalidPlaceholder(buffer, "{", &placeholderValue, i, formatstr->length);
|
||||||
@@ -583,7 +642,7 @@ static bool parseFormatString(FFstrbuf* buffer, const FFstrbuf* formatstr, uint3
|
|||||||
bool ellipsis = false;
|
bool ellipsis = false;
|
||||||
if (truncLength < 0) {
|
if (truncLength < 0) {
|
||||||
ellipsis = true;
|
ellipsis = true;
|
||||||
truncLength = -truncLength;
|
truncLength = truncLength == INT32_MIN ? INT32_MAX : -truncLength;
|
||||||
}
|
}
|
||||||
|
|
||||||
FF_STRBUF_AUTO_DESTROY tempString = ffStrbufCreate();
|
FF_STRBUF_AUTO_DESTROY tempString = ffStrbufCreate();
|
||||||
|
|||||||
+36
-21
@@ -149,6 +149,32 @@ void restoreTerm(void) {
|
|||||||
tcsetattr(ftty, TCSAFLUSH, &oldTerm);
|
tcsetattr(ftty, TCSAFLUSH, &oldTerm);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static const char* waitForTerminalResponse(void) {
|
||||||
|
#ifndef __APPLE__
|
||||||
|
const int pollResult = poll(&(struct pollfd) { .fd = ftty, .events = POLLIN }, 1, FF_IO_TERM_RESP_WAIT_MS);
|
||||||
|
if (pollResult <= 0) {
|
||||||
|
FF_DEBUG("poll(/dev/tty) returned %d after %d ms: %s",
|
||||||
|
pollResult, FF_IO_TERM_RESP_WAIT_MS, pollResult == 0 ? "timed out" : strerror(errno));
|
||||||
|
return "poll(/dev/tty) timeout or failed";
|
||||||
|
}
|
||||||
|
#else
|
||||||
|
{
|
||||||
|
// On macOS, poll(/dev/tty) always returns immediately
|
||||||
|
// See also https://nathancraddock.com/blog/macos-dev-tty-polling/
|
||||||
|
fd_set rd;
|
||||||
|
FD_ZERO(&rd);
|
||||||
|
FD_SET(ftty, &rd);
|
||||||
|
const int selectResult = select(ftty + 1, &rd, nullptr, nullptr, &(struct timeval) { .tv_sec = FF_IO_TERM_RESP_WAIT_MS / 1000, .tv_usec = (FF_IO_TERM_RESP_WAIT_MS % 1000) * 1000 });
|
||||||
|
if (selectResult <= 0) {
|
||||||
|
FF_DEBUG("select(/dev/tty) returned %d after %d ms: %s",
|
||||||
|
selectResult, FF_IO_TERM_RESP_WAIT_MS, selectResult == 0 ? "timed out" : strerror(errno));
|
||||||
|
return "select(/dev/tty) timeout or failed";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
return nullptr;
|
||||||
|
}
|
||||||
|
|
||||||
const char* ffGetTerminalResponse(const char* request, int nParams, const char* format, ...) {
|
const char* ffGetTerminalResponse(const char* request, int nParams, const char* format, ...) {
|
||||||
if (ftty < 0) {
|
if (ftty < 0) {
|
||||||
ftty = open("/dev/tty", O_RDWR | O_NOCTTY | O_CLOEXEC);
|
ftty = open("/dev/tty", O_RDWR | O_NOCTTY | O_CLOEXEC);
|
||||||
@@ -174,28 +200,10 @@ const char* ffGetTerminalResponse(const char* request, int nParams, const char*
|
|||||||
ffWriteFDData(ftty, strlen(request), request);
|
ffWriteFDData(ftty, strlen(request), request);
|
||||||
|
|
||||||
// Give the terminal some time to respond
|
// Give the terminal some time to respond
|
||||||
#ifndef __APPLE__
|
const char* error = waitForTerminalResponse();
|
||||||
const int pollResult = poll(&(struct pollfd) { .fd = ftty, .events = POLLIN }, 1, FF_IO_TERM_RESP_WAIT_MS);
|
if (error) {
|
||||||
if (pollResult <= 0) {
|
return error;
|
||||||
FF_DEBUG("poll(/dev/tty) returned %d after %d ms: %s",
|
|
||||||
pollResult, FF_IO_TERM_RESP_WAIT_MS, pollResult == 0 ? "timed out" : strerror(errno));
|
|
||||||
return "poll(/dev/tty) timeout or failed";
|
|
||||||
}
|
}
|
||||||
#else
|
|
||||||
{
|
|
||||||
// On macOS, poll(/dev/tty) always returns immediately
|
|
||||||
// See also https://nathancraddock.com/blog/macos-dev-tty-polling/
|
|
||||||
fd_set rd;
|
|
||||||
FD_ZERO(&rd);
|
|
||||||
FD_SET(ftty, &rd);
|
|
||||||
const int selectResult = select(ftty + 1, &rd, nullptr, nullptr, &(struct timeval) { .tv_sec = FF_IO_TERM_RESP_WAIT_MS / 1000, .tv_usec = (FF_IO_TERM_RESP_WAIT_MS % 1000) * 1000 });
|
|
||||||
if (selectResult <= 0) {
|
|
||||||
FF_DEBUG("select(/dev/tty) returned %d after %d ms: %s",
|
|
||||||
selectResult, FF_IO_TERM_RESP_WAIT_MS, selectResult == 0 ? "timed out" : strerror(errno));
|
|
||||||
return "select(/dev/tty) timeout or failed";
|
|
||||||
}
|
|
||||||
}
|
|
||||||
#endif
|
|
||||||
|
|
||||||
char buffer[1024];
|
char buffer[1024];
|
||||||
size_t bytesRead = 0;
|
size_t bytesRead = 0;
|
||||||
@@ -204,6 +212,13 @@ const char* ffGetTerminalResponse(const char* request, int nParams, const char*
|
|||||||
va_start(args, format);
|
va_start(args, format);
|
||||||
|
|
||||||
while (true) {
|
while (true) {
|
||||||
|
// The response may arrive in several chunks; a terminal that sends an incomplete
|
||||||
|
// response would otherwise block `read` forever
|
||||||
|
if (bytesRead > 0 && (error = waitForTerminalResponse())) {
|
||||||
|
va_end(args);
|
||||||
|
return error;
|
||||||
|
}
|
||||||
|
|
||||||
ssize_t nRead = read(ftty, buffer + bytesRead, sizeof(buffer) - bytesRead - 1);
|
ssize_t nRead = read(ftty, buffer + bytesRead, sizeof(buffer) - bytesRead - 1);
|
||||||
|
|
||||||
if (nRead <= 0) {
|
if (nRead <= 0) {
|
||||||
|
|||||||
@@ -414,6 +414,35 @@ void ffListFilesRecursively(const char* path, bool pretty) {
|
|||||||
listFilesRecursively(folder.length, &folder, 0, nullptr, pretty);
|
listFilesRecursively(folder.length, &folder, 0, nullptr, pretty);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static const char* waitForTerminalResponse(HANDLE hInput) {
|
||||||
|
while (true) {
|
||||||
|
NTSTATUS status = NtWaitForSingleObject(hInput, FALSE, &(LARGE_INTEGER) { .QuadPart = (int64_t) FF_IO_TERM_RESP_WAIT_MS * -10000 });
|
||||||
|
if (status != STATUS_WAIT_0) {
|
||||||
|
FF_DEBUG("NtWaitForSingleObject() failed or timeout: %s", ffDebugNtStatus(status));
|
||||||
|
return "NtWaitForSingleObject() failed or timeout";
|
||||||
|
}
|
||||||
|
|
||||||
|
// Ignore all unexpected input events
|
||||||
|
INPUT_RECORD record;
|
||||||
|
DWORD len = 0;
|
||||||
|
if (!PeekConsoleInputW(hInput, &record, 1, &len)) {
|
||||||
|
return nullptr;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Key-up events carry no input for `ReadFile`; leaving one in the buffer would make the
|
||||||
|
// wait return while the next `ReadFile` still blocks
|
||||||
|
if (
|
||||||
|
record.EventType == KEY_EVENT &&
|
||||||
|
record.Event.KeyEvent.bKeyDown &&
|
||||||
|
record.Event.KeyEvent.uChar.UnicodeChar != L'\r' &&
|
||||||
|
record.Event.KeyEvent.uChar.UnicodeChar != L'\n') {
|
||||||
|
return nullptr;
|
||||||
|
}
|
||||||
|
|
||||||
|
ReadConsoleInputW(hInput, &record, 1, &len);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
const char* ffGetTerminalResponse(const char* request, int nParams, const char* format, ...) {
|
const char* ffGetTerminalResponse(const char* request, int nParams, const char* format, ...) {
|
||||||
HANDLE hInput = GetStdHandle(STD_INPUT_HANDLE);
|
HANDLE hInput = GetStdHandle(STD_INPUT_HANDLE);
|
||||||
FF_AUTO_CLOSE_FD HANDLE hConin = INVALID_HANDLE_VALUE;
|
FF_AUTO_CLOSE_FD HANDLE hConin = INVALID_HANDLE_VALUE;
|
||||||
@@ -440,29 +469,10 @@ const char* ffGetTerminalResponse(const char* request, int nParams, const char*
|
|||||||
WriteFile(hOutput, request, (DWORD) strlen(request), &bytes, nullptr);
|
WriteFile(hOutput, request, (DWORD) strlen(request), &bytes, nullptr);
|
||||||
}
|
}
|
||||||
|
|
||||||
while (true) {
|
const char* error = waitForTerminalResponse(hInput);
|
||||||
NTSTATUS status = NtWaitForSingleObject(hInput, FALSE, &(LARGE_INTEGER) { .QuadPart = (int64_t) FF_IO_TERM_RESP_WAIT_MS * -10000 });
|
if (error) {
|
||||||
if (status != STATUS_WAIT_0) {
|
|
||||||
FF_DEBUG("NtWaitForSingleObject() failed or timeout: %s", ffDebugNtStatus(status));
|
|
||||||
SetConsoleMode(hInput, inputMode);
|
SetConsoleMode(hInput, inputMode);
|
||||||
return "NtWaitForSingleObject() failed or timeout";
|
return error;
|
||||||
}
|
|
||||||
|
|
||||||
// Ignore all unexpected input events
|
|
||||||
INPUT_RECORD record;
|
|
||||||
DWORD len = 0;
|
|
||||||
if (!PeekConsoleInputW(hInput, &record, 1, &len)) {
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (
|
|
||||||
record.EventType == KEY_EVENT &&
|
|
||||||
record.Event.KeyEvent.uChar.UnicodeChar != L'\r' &&
|
|
||||||
record.Event.KeyEvent.uChar.UnicodeChar != L'\n') {
|
|
||||||
break;
|
|
||||||
} else {
|
|
||||||
ReadConsoleInputW(hInput, &record, 1, &len);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
va_list args;
|
va_list args;
|
||||||
@@ -472,6 +482,16 @@ const char* ffGetTerminalResponse(const char* request, int nParams, const char*
|
|||||||
uint32_t bytesRead = 0;
|
uint32_t bytesRead = 0;
|
||||||
|
|
||||||
while (true) {
|
while (true) {
|
||||||
|
// The response may arrive in several chunks; a terminal that sends an incomplete
|
||||||
|
// response would otherwise block `ReadFile` forever
|
||||||
|
if (bytesRead > 0 && (error = waitForTerminalResponse(hInput))) {
|
||||||
|
va_end(args);
|
||||||
|
if (hasInputMode) {
|
||||||
|
SetConsoleMode(hInput, inputMode);
|
||||||
|
}
|
||||||
|
return error;
|
||||||
|
}
|
||||||
|
|
||||||
DWORD bytes = 0;
|
DWORD bytes = 0;
|
||||||
BOOL readOk = ReadFile(hInput, buffer + bytesRead, (DWORD) (sizeof(buffer) - 1 - bytesRead), &bytes, nullptr);
|
BOOL readOk = ReadFile(hInput, buffer + bytesRead, (DWORD) (sizeof(buffer) - 1 - bytesRead), &bytes, nullptr);
|
||||||
if (!readOk || bytes == 0) {
|
if (!readOk || bytes == 0) {
|
||||||
|
|||||||
+226
-55
@@ -411,81 +411,246 @@ void ffCPUDetectByCpuid(FFCPUResult* cpu) {
|
|||||||
#undef HAS_CAP
|
#undef HAS_CAP
|
||||||
}
|
}
|
||||||
#elif _WIN32
|
#elif _WIN32
|
||||||
#include <processthreadsapi.h>
|
#include "common/windows/registry.h"
|
||||||
|
|
||||||
// Missing from winnt.h of MinGW-w64
|
// Windows exposes the AArch64 ID registers in the registry, as values named
|
||||||
#define PF_ARM_LSE2_AVAILABLE 62
|
// "CP xxxx" under HARDWARE\DESCRIPTION\System\CentralProcessor\0. Each value is
|
||||||
#define PF_RESERVED_FEATURE 63
|
// the 64-bit content of one ID register; decode the same fields that the Linux
|
||||||
#define PF_ARM_SHA3_INSTRUCTIONS_AVAILABLE 64
|
// HWCAP based detection below is built from.
|
||||||
#define PF_ARM_SHA512_INSTRUCTIONS_AVAILABLE 65
|
//
|
||||||
#define PF_ARM_V82_I8MM_INSTRUCTIONS_AVAILABLE 66
|
// Where "xxxx" comes from
|
||||||
#define PF_ARM_V82_FP16_INSTRUCTIONS_AVAILABLE 67
|
// -----------------------
|
||||||
#define PF_ARM_V86_BF16_INSTRUCTIONS_AVAILABLE 68
|
// The four hex digits encode the AArch64 system register name S3_0_C0_CRm_op2
|
||||||
#define PF_ARM_V86_EBF16_INSTRUCTIONS_AVAILABLE 69
|
// (all ID registers live in CRn = C0 and use op0 = 3):
|
||||||
#define PF_ARM_SME_INSTRUCTIONS_AVAILABLE 70
|
//
|
||||||
#define PF_ARM_SME2_INSTRUCTIONS_AVAILABLE 71
|
// xxxx = 0x4000 + (CRm << 3) + op2
|
||||||
#define PF_ARM_SME2_1_INSTRUCTIONS_AVAILABLE 72
|
//
|
||||||
#define PF_ARM_SME2_2_INSTRUCTIONS_AVAILABLE 73
|
// e.g. ID_AA64PFR0_EL1 = S3_0_C0_C4_0 -> 0x4000 + (4 << 3) + 0 = 0x4020,
|
||||||
#define PF_ARM_SME_AES_INSTRUCTIONS_AVAILABLE 74
|
// ID_AA64ISAR1_EL1 = S3_0_C0_C6_1 -> 0x4000 + (6 << 3) + 1 = 0x4031.
|
||||||
#define PF_ARM_SME_SBITPERM_INSTRUCTIONS_AVAILABLE 75
|
// The full list is in the ARM Architecture Reference Manual, chapter "AArch64
|
||||||
#define PF_ARM_SME_SF8MM4_INSTRUCTIONS_AVAILABLE 76
|
// System Register Encoding". Linux has the same encodings in sysreg.h as
|
||||||
#define PF_ARM_SME_SF8MM8_INSTRUCTIONS_AVAILABLE 77
|
// SYS_ID_AA64*_EL1; Windows just prints the encoding instead of the name.
|
||||||
#define PF_ARM_SME_SF8DP2_INSTRUCTIONS_AVAILABLE 78
|
//
|
||||||
#define PF_ARM_SME_SF8DP4_INSTRUCTIONS_AVAILABLE 79
|
// Where the offsets / minimums come from
|
||||||
#define PF_ARM_SME_SF8FMA_INSTRUCTIONS_AVAILABLE 80
|
// --------------------------------------
|
||||||
#define PF_ARM_SME_F8F32_INSTRUCTIONS_AVAILABLE 81
|
// Every field offset below is the LSB of that field in the ARM ARM description
|
||||||
#define PF_ARM_SME_F8F16_INSTRUCTIONS_AVAILABLE 82
|
// of the ID register (also SYS_ID_AA64*_EL1_*_SHIFT in Linux sysreg.h).
|
||||||
#define PF_ARM_SME_F16F16_INSTRUCTIONS_AVAILABLE 83
|
// Every minimum is the lowest field value that guarantees the corresponding
|
||||||
#define PF_ARM_SME_B16B16_INSTRUCTIONS_AVAILABLE 84
|
// instruction/feature (the field value tables in the ARM ARM, mirrored by the
|
||||||
#define PF_ARM_SME_F64F64_INSTRUCTIONS_AVAILABLE 85
|
// cpufeature.c checks that set the matching HWCAP/HWCAP2 bit):
|
||||||
#define PF_ARM_SME_I16I64_INSTRUCTIONS_AVAILABLE 86
|
//
|
||||||
#define PF_ARM_SME_LUTv2_INSTRUCTIONS_AVAILABLE 87
|
// value 0b0000 baseline value; usually "not implemented", but for fields
|
||||||
#define PF_ARM_SME_FA64_INSTRUCTIONS_AVAILABLE 88
|
// that describe an always-present form it names that form
|
||||||
|
// (FP/AdvSIMD: implemented; SVEver: SVE; SMEver: SME)
|
||||||
|
// value 0b0001 first extension level for most fields; SVEver 1 is SVE2
|
||||||
|
// value 0b0010 second level; ATOMIC, LRCPC, DPB, FlagM, PFR1.SME, CSSC
|
||||||
|
// and SMFR0.SMEver use >= 2 for their newer form
|
||||||
|
// value 0b1111 reserved "not implemented" marker; a field holding 0xF
|
||||||
|
// must never be treated as supporting the feature.
|
||||||
|
//
|
||||||
|
// armField() therefore requires both "field != 0xF" and
|
||||||
|
// "field >= minimum". minimum == 0 is used only where the mere presence of an
|
||||||
|
// implemented field is enough (PFR0.FP and PFR0.AdvSIMD for the ARMv8-A
|
||||||
|
// baseline). A register can be missing on some Windows builds/CPUs; a missing
|
||||||
|
// register makes all of its features false and thus lowers the reported
|
||||||
|
// version instead of reporting a wrong higher one.
|
||||||
|
//
|
||||||
|
// Not every field is a 4-bit value: ID_AA64FPFR0_EL1 (ARMv9.5 FP8) only
|
||||||
|
// defines individual feature bits, so those checks use armBit().
|
||||||
|
//
|
||||||
|
// To add a new instruction/feature in the future
|
||||||
|
// ----------------------------------------------
|
||||||
|
// 1. Find the ID register and field that advertises it in the ARM ARM (and in
|
||||||
|
// the matching Linux HWCAP/HWCAP2 definition).
|
||||||
|
// 2. If that register is not read yet, add an FF_ARM_REG_* entry before
|
||||||
|
// FF_ARM_REG_COUNT and its "CP xxxx" name in armRegisterNames[]; the xxxx is
|
||||||
|
// computed with the formula above. Registers are read automatically because
|
||||||
|
// armReadRegisters() loops over the whole table.
|
||||||
|
// 3. Add a bool using armField(regs, present, FF_ARM_REG_..., offset, minimum)
|
||||||
|
// or armBit(regs, present, FF_ARM_REG_..., bit). Put the register/field name
|
||||||
|
// and the Linux HWCAP bit it mirrors in a comment.
|
||||||
|
// 4. Insert the condition into the correct version's place in the if/else if
|
||||||
|
// chain below, keeping the order identical to the Linux implementation so
|
||||||
|
// both platforms report the same march. A newly required feature belongs to
|
||||||
|
// the version that introduced it; a feature that only raises the baseline
|
||||||
|
// is an extra `else if` above the older checks.
|
||||||
|
// 5. Rebuild and check with `fastfetch -s cpu --format json`; compare against
|
||||||
|
// Linux on the same machine when possible.
|
||||||
|
|
||||||
|
typedef enum FFCPUArmRegister {
|
||||||
|
FF_ARM_REG_PFR0,
|
||||||
|
FF_ARM_REG_PFR1,
|
||||||
|
FF_ARM_REG_ZFR0,
|
||||||
|
FF_ARM_REG_SMFR0,
|
||||||
|
FF_ARM_REG_FPFR0,
|
||||||
|
FF_ARM_REG_ISAR0,
|
||||||
|
FF_ARM_REG_ISAR1,
|
||||||
|
FF_ARM_REG_ISAR2,
|
||||||
|
FF_ARM_REG_ISAR3,
|
||||||
|
FF_ARM_REG_MMFR1,
|
||||||
|
FF_ARM_REG_COUNT,
|
||||||
|
} FFCPUArmRegister;
|
||||||
|
|
||||||
|
static const wchar_t* const armRegisterNames[FF_ARM_REG_COUNT] = {
|
||||||
|
[FF_ARM_REG_PFR0] = L"CP 4020", // ID_AA64PFR0_EL1
|
||||||
|
[FF_ARM_REG_PFR1] = L"CP 4021", // ID_AA64PFR1_EL1
|
||||||
|
[FF_ARM_REG_ZFR0] = L"CP 4024", // ID_AA64ZFR0_EL1
|
||||||
|
[FF_ARM_REG_SMFR0] = L"CP 4025", // ID_AA64SMFR0_EL1
|
||||||
|
[FF_ARM_REG_FPFR0] = L"CP 4027", // ID_AA64FPFR0_EL1
|
||||||
|
[FF_ARM_REG_ISAR0] = L"CP 4030", // ID_AA64ISAR0_EL1
|
||||||
|
[FF_ARM_REG_ISAR1] = L"CP 4031", // ID_AA64ISAR1_EL1
|
||||||
|
[FF_ARM_REG_ISAR2] = L"CP 4032", // ID_AA64ISAR2_EL1
|
||||||
|
[FF_ARM_REG_ISAR3] = L"CP 4033", // ID_AA64ISAR3_EL1
|
||||||
|
[FF_ARM_REG_MMFR1] = L"CP 4039", // ID_AA64MMFR1_EL1
|
||||||
|
};
|
||||||
|
|
||||||
|
static bool armReadRegisters(uint64_t regs[FF_ARM_REG_COUNT], bool present[FF_ARM_REG_COUNT]) {
|
||||||
|
FF_AUTO_CLOSE_FD HANDLE hKey = nullptr;
|
||||||
|
if (!ffRegOpenKeyForRead(HKEY_LOCAL_MACHINE, L"HARDWARE\\DESCRIPTION\\System\\CentralProcessor\\0", &hKey, nullptr)) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool anyData = false;
|
||||||
|
for (uint32_t i = 0; i < FF_ARM_REG_COUNT; ++i) {
|
||||||
|
uint64_t value = 0;
|
||||||
|
if (ffRegReadUint64(hKey, armRegisterNames[i], &value, nullptr)) {
|
||||||
|
regs[i] = value;
|
||||||
|
present[i] = true;
|
||||||
|
anyData = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return anyData;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Same rule as the Linux implementation: a 4-bit field is present when it is
|
||||||
|
// neither 0xF (not implemented) nor below the value required by the feature.
|
||||||
|
// `minimum` is the ARM ARM field value that first guarantees the feature; see
|
||||||
|
// the magic number explanation above.
|
||||||
|
static bool armField(
|
||||||
|
const uint64_t regs[FF_ARM_REG_COUNT],
|
||||||
|
const bool present[FF_ARM_REG_COUNT],
|
||||||
|
FFCPUArmRegister reg,
|
||||||
|
uint8_t offset,
|
||||||
|
uint8_t minimum) {
|
||||||
|
if (!present[reg]) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
uint8_t value = (uint8_t) ((regs[reg] >> offset) & 0xF);
|
||||||
|
return value != 0xF && value >= minimum;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Single-bit feature fields (currently only ID_AA64FPFR0_EL1). Prefer
|
||||||
|
// armField() for the normal 4-bit ID fields.
|
||||||
|
static bool armBit(
|
||||||
|
const uint64_t regs[FF_ARM_REG_COUNT],
|
||||||
|
const bool present[FF_ARM_REG_COUNT],
|
||||||
|
FFCPUArmRegister reg,
|
||||||
|
uint8_t bit) {
|
||||||
|
return present[reg] && ((regs[reg] >> bit) & 1) != 0;
|
||||||
|
}
|
||||||
|
|
||||||
void ffCPUDetectByCpuid(FFCPUResult* cpu) {
|
void ffCPUDetectByCpuid(FFCPUResult* cpu) {
|
||||||
|
uint64_t regs[FF_ARM_REG_COUNT] = {};
|
||||||
|
bool present[FF_ARM_REG_COUNT] = {};
|
||||||
|
|
||||||
|
if (!armReadRegisters(regs, present)) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// ID_AA64ISAR1_EL1.APA [7:4] describes QARMA5 address authentication,
|
||||||
|
// while API [11:8] describes the implementation-defined variant. They are
|
||||||
|
// mutually exclusive but share the same encoding. Normalize API into the
|
||||||
|
// APA slot so both implementations are detected (Apple Silicon reports
|
||||||
|
// APA = 0 and API = 2).
|
||||||
|
uint64_t isar1 = regs[FF_ARM_REG_ISAR1];
|
||||||
|
uint8_t apa = (uint8_t) ((isar1 >> 4) & 0xF);
|
||||||
|
uint8_t api = (uint8_t) ((isar1 >> 8) & 0xF);
|
||||||
|
if (apa == 0 && api != 0) {
|
||||||
|
isar1 = (isar1 & ~(UINT64_C(0xF) << 4)) | ((uint64_t) api << 4);
|
||||||
|
regs[FF_ARM_REG_ISAR1] = isar1;
|
||||||
|
}
|
||||||
|
|
||||||
|
cpu->march = "unknown";
|
||||||
|
|
||||||
// ARMv8-A
|
// ARMv8-A
|
||||||
bool has_vfp = IsProcessorFeaturePresent(PF_ARM_VFP_32_REGISTERS_AVAILABLE); // Implies basic FP support
|
bool has_fp = armField(regs, present, FF_ARM_REG_PFR0, 16, 0); // PFR0.FP[19:16] implemented -> HWCAP_FP
|
||||||
bool has_neon = IsProcessorFeaturePresent(PF_ARM_NEON_INSTRUCTIONS_AVAILABLE); // NEON (ASIMD)
|
bool has_asimd = armField(regs, present, FF_ARM_REG_PFR0, 20, 0); // PFR0.AdvSIMD[23:20] implemented -> HWCAP_ASIMD
|
||||||
|
|
||||||
// ARMv8.1-A
|
// ARMv8.1-A
|
||||||
bool has_atomics = IsProcessorFeaturePresent(PF_ARM_V81_ATOMIC_INSTRUCTIONS_AVAILABLE); // LSE atomics
|
bool has_atomics = armField(regs, present, FF_ARM_REG_ISAR0, 20, 2); // ISAR0.ATOMIC[23:20] >= 2 -> HWCAP_ATOMICS
|
||||||
bool has_crc32 = IsProcessorFeaturePresent(PF_ARM_V8_CRC32_INSTRUCTIONS_AVAILABLE); // CRC32
|
bool has_crc32 = armField(regs, present, FF_ARM_REG_ISAR0, 16, 1); // ISAR0.CRC32[19:16] >= 1 -> HWCAP_CRC32
|
||||||
|
bool has_asimdrdm = armField(regs, present, FF_ARM_REG_ISAR0, 28, 1); // ISAR0.RDM[31:28] >= 1 -> HWCAP_ASIMDRDM
|
||||||
|
|
||||||
// ARMv8.2-A
|
// ARMv8.2-A
|
||||||
bool has_fp16 = IsProcessorFeaturePresent(PF_ARM_V82_FP16_INSTRUCTIONS_AVAILABLE); // Half-precision FP
|
bool has_fphp = armField(regs, present, FF_ARM_REG_PFR0, 16, 1); // PFR0.FP[19:16] >= 1 -> HWCAP_FPHP
|
||||||
|
bool has_dcpop = armField(regs, present, FF_ARM_REG_ISAR1, 0, 1); // ISAR1.DPB[3:0] >= 1 -> HWCAP_DCPOP
|
||||||
|
|
||||||
// ARMv8.3-A
|
// ARMv8.3-A
|
||||||
bool has_lrcpc = IsProcessorFeaturePresent(PF_ARM_V83_LRCPC_INSTRUCTIONS_AVAILABLE); // LDAPR/LR with RCPC semantics
|
bool has_paca = armField(regs, present, FF_ARM_REG_ISAR1, 4, 1) // ISAR1.APA[7:4] >= 1 -> HWCAP_PACA
|
||||||
bool has_jscvt = IsProcessorFeaturePresent(PF_ARM_V83_JSCVT_INSTRUCTIONS_AVAILABLE); // FJCVTZS
|
|| armField(regs, present, FF_ARM_REG_ISAR2, 12, 1); // ISAR2.APA3[15:12] >= 1 (newer encoding)
|
||||||
|
bool has_lrcpc = armField(regs, present, FF_ARM_REG_ISAR1, 20, 1); // ISAR1.LRCPC[23:20] >= 1 -> HWCAP_LRCPC
|
||||||
|
bool has_fcma = armField(regs, present, FF_ARM_REG_ISAR1, 16, 1); // ISAR1.FCMA[19:16] >= 1 -> HWCAP_FCMA
|
||||||
|
bool has_jscvt = armField(regs, present, FF_ARM_REG_ISAR1, 12, 1); // ISAR1.JSCVT[15:12] >= 1 -> HWCAP_JSCVT
|
||||||
|
|
||||||
// ARMv8.4-A
|
// ARMv8.4-A
|
||||||
// My CPU (Apple M1 Pro in VM) does support LSE2, but Windows doesn't detect it for some reason
|
bool has_dit = armField(regs, present, FF_ARM_REG_PFR0, 48, 1); // PFR0.DIT[51:48] >= 1 -> HWCAP_DIT
|
||||||
bool has_lse2 = IsProcessorFeaturePresent(PF_ARM_LSE2_AVAILABLE); // Large System Extensions version 2, optional from v8.2
|
bool has_flagm = armField(regs, present, FF_ARM_REG_ISAR0, 52, 1); // ISAR0.FlagM[55:52] >= 1 -> HWCAP_FLAGM
|
||||||
bool has_dp = IsProcessorFeaturePresent(PF_ARM_V82_DP_INSTRUCTIONS_AVAILABLE); // DotProd, optional from v8.1 (*)
|
bool has_ilrcpc = armField(regs, present, FF_ARM_REG_ISAR1, 20, 2); // ISAR1.LRCPC[23:20] >= 2 -> HWCAP_ILRCPC
|
||||||
|
|
||||||
|
// ARMv8.5-A
|
||||||
|
bool has_bti = armField(regs, present, FF_ARM_REG_PFR1, 0, 1); // PFR1.BT[3:0] >= 1 -> HWCAP2_BTI
|
||||||
|
bool has_sb = armField(regs, present, FF_ARM_REG_ISAR1, 36, 1); // ISAR1.SB[39:36] >= 1 -> HWCAP_SB
|
||||||
|
bool has_dcpodp = armField(regs, present, FF_ARM_REG_ISAR1, 0, 2); // ISAR1.DPB[3:0] >= 2 -> HWCAP2_DCPODP
|
||||||
|
bool has_flagm2 = armField(regs, present, FF_ARM_REG_ISAR0, 52, 2); // ISAR0.FlagM[55:52] >= 2 -> HWCAP2_FLAGM2
|
||||||
|
bool has_frint = armField(regs, present, FF_ARM_REG_ISAR1, 32, 1); // ISAR1.FRINTTS[35:32] >= 1 -> HWCAP2_FRINT
|
||||||
|
|
||||||
// ARMv9.0-A
|
// ARMv9.0-A
|
||||||
bool has_sve2 = IsProcessorFeaturePresent(PF_ARM_SVE2_INSTRUCTIONS_AVAILABLE); // SVE2
|
bool has_sve2 = armField(regs, present, FF_ARM_REG_ZFR0, 0, 1); // ZFR0.SVEver[3:0] >= 1 (1 = SVE2) -> HWCAP2_SVE2
|
||||||
|
|
||||||
// ARMv9.1-A
|
// ARMv9.1-A
|
||||||
// ARMv8.6-A
|
// ARMv8.6-A
|
||||||
bool has_bf16 = IsProcessorFeaturePresent(PF_ARM_V86_BF16_INSTRUCTIONS_AVAILABLE); // BF16, optional from v8.2
|
// BF16/I8MM are shared by both version sequences, so the same booleans are
|
||||||
bool has_i8mm = IsProcessorFeaturePresent(PF_ARM_V82_I8MM_INSTRUCTIONS_AVAILABLE); // Int8 matrix multiply, optional from v8.2
|
// tested in both branches of the chain below.
|
||||||
|
bool has_bf16 = armField(regs, present, FF_ARM_REG_ISAR1, 44, 1); // ISAR1.BF16[47:44] >= 1 -> HWCAP2_BF16
|
||||||
|
bool has_i8mm = armField(regs, present, FF_ARM_REG_ISAR1, 52, 1); // ISAR1.I8MM[55:52] >= 1 -> HWCAP2_I8MM
|
||||||
|
|
||||||
// ARMv8.7-A
|
// ARMv8.7-A
|
||||||
bool has_ebf16 = IsProcessorFeaturePresent(PF_ARM_V86_EBF16_INSTRUCTIONS_AVAILABLE); // Extended BFloat16 behaviors, optional from v8.2
|
bool has_afp = armField(regs, present, FF_ARM_REG_MMFR1, 44, 1); // MMFR1.AFP[47:44] >= 1 -> HWCAP2_AFP
|
||||||
|
|
||||||
// ARMv9.2-A
|
// ARMv9.2-A
|
||||||
bool has_sme = IsProcessorFeaturePresent(PF_ARM_SME_INSTRUCTIONS_AVAILABLE); // SME
|
bool has_sme = armField(regs, present, FF_ARM_REG_PFR1, 24, 1); // PFR1.SME[27:24] >= 1 -> HWCAP2_SME
|
||||||
|
|
||||||
// ARMv9.3-A
|
// ARMv9.3-A
|
||||||
bool has_sme2 = IsProcessorFeaturePresent(PF_ARM_SME2_INSTRUCTIONS_AVAILABLE); // SME2
|
bool has_sme2 = armField(regs, present, FF_ARM_REG_PFR1, 24, 2); // PFR1.SME[27:24] >= 2 -> HWCAP2_SME2
|
||||||
|
|
||||||
|
// ARMv8.8-A
|
||||||
|
bool has_mops = armField(regs, present, FF_ARM_REG_ISAR2, 16, 1); // ISAR2.MOPS[19:16] >= 1 -> HWCAP2_MOPS
|
||||||
|
|
||||||
|
// ARMv8.9-A
|
||||||
|
bool has_cssc = armField(regs, present, FF_ARM_REG_ISAR2, 52, 1); // ISAR2.CSSC[55:52] >= 1 -> HWCAP2_CSSC
|
||||||
|
|
||||||
// ARMv9.4-A
|
// ARMv9.4-A
|
||||||
bool has_sme2p1 = IsProcessorFeaturePresent(PF_ARM_SME2_1_INSTRUCTIONS_AVAILABLE); // SME2.1
|
bool has_sme2p1 = armField(regs, present, FF_ARM_REG_SMFR0, 56, 2); // SMFR0.SMEver[59:56] >= 2 -> HWCAP2_SME2P1
|
||||||
|
|
||||||
|
// ARMv9.5-A
|
||||||
|
bool has_f8e4m3 = armBit(regs, present, FF_ARM_REG_FPFR0, 1); // FPFR0.F8E4M3[1] -> HWCAP2_F8E4M3
|
||||||
|
bool has_f8e5m2 = armBit(regs, present, FF_ARM_REG_FPFR0, 0); // FPFR0.F8E5M2[0] -> HWCAP2_F8E5M2
|
||||||
|
|
||||||
|
// ARMv9.6-A
|
||||||
|
bool has_cmpbr = armField(regs, present, FF_ARM_REG_ISAR2, 52, 2); // ISAR2.CSSC[55:52] >= 2 -> HWCAP_CMPBR
|
||||||
|
bool has_fprcvt = armField(regs, present, FF_ARM_REG_ISAR3, 28, 1); // ISAR3.FPRCVT[31:28] >= 1 -> HWCAP_FPRCVT
|
||||||
|
|
||||||
|
// The version chain (both the ARMv9/ARMv8 split and the order of the
|
||||||
|
// checks) must stay identical to the Linux implementation above, otherwise
|
||||||
|
// the same CPU reports a different march depending on the OS.
|
||||||
if (has_sve2 || has_sme) {
|
if (has_sve2 || has_sme) {
|
||||||
// ARMv9 family
|
// ARMv9
|
||||||
if (has_sme2p1) {
|
if (has_cmpbr && has_fprcvt) {
|
||||||
|
cpu->march = "ARMv9.6-A";
|
||||||
|
} else if (has_f8e5m2 && has_f8e4m3) {
|
||||||
|
cpu->march = "ARMv9.5-A";
|
||||||
|
} else if (has_sme2p1) {
|
||||||
cpu->march = "ARMv9.4-A";
|
cpu->march = "ARMv9.4-A";
|
||||||
} else if (has_sme2) {
|
} else if (has_sme2) {
|
||||||
cpu->march = "ARMv9.3-A";
|
cpu->march = "ARMv9.3-A";
|
||||||
@@ -497,20 +662,26 @@ void ffCPUDetectByCpuid(FFCPUResult* cpu) {
|
|||||||
cpu->march = "ARMv9.0-A";
|
cpu->march = "ARMv9.0-A";
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
// ARMv8 family
|
// ARMv8
|
||||||
if (has_ebf16) {
|
if (has_cssc) {
|
||||||
|
cpu->march = "ARMv8.9-A";
|
||||||
|
} else if (has_mops) {
|
||||||
|
cpu->march = "ARMv8.8-A";
|
||||||
|
} else if (has_afp) {
|
||||||
cpu->march = "ARMv8.7-A";
|
cpu->march = "ARMv8.7-A";
|
||||||
} else if (has_i8mm && has_bf16) {
|
} else if (has_i8mm && has_bf16) {
|
||||||
cpu->march = "ARMv8.6-A";
|
cpu->march = "ARMv8.6-A";
|
||||||
} else if (has_dp && has_lse2) {
|
} else if (has_bti && has_sb && has_dcpodp && has_flagm2 && has_frint) {
|
||||||
|
cpu->march = "ARMv8.5-A";
|
||||||
|
} else if (has_dit && has_flagm && has_ilrcpc) {
|
||||||
cpu->march = "ARMv8.4-A";
|
cpu->march = "ARMv8.4-A";
|
||||||
} else if (has_lrcpc && has_jscvt) {
|
} else if (has_paca && has_lrcpc && has_fcma && has_jscvt) {
|
||||||
cpu->march = "ARMv8.3-A";
|
cpu->march = "ARMv8.3-A";
|
||||||
} else if (has_fp16) {
|
} else if (has_fphp && has_dcpop) {
|
||||||
cpu->march = "ARMv8.2-A";
|
cpu->march = "ARMv8.2-A";
|
||||||
} else if (has_atomics && has_crc32) {
|
} else if (has_atomics && has_crc32 && has_asimdrdm) {
|
||||||
cpu->march = "ARMv8.1-A";
|
cpu->march = "ARMv8.1-A";
|
||||||
} else if (has_neon && has_vfp) {
|
} else if (has_asimd && has_fp) {
|
||||||
cpu->march = "ARMv8-A";
|
cpu->march = "ARMv8-A";
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -0,0 +1,425 @@
|
|||||||
|
#include "wayland.h"
|
||||||
|
|
||||||
|
#if defined(FF_HAVE_WAYLAND) && defined(FF_HAVE_DBUS) && defined(__linux__)
|
||||||
|
|
||||||
|
#include "common/arrutil.h"
|
||||||
|
#include "common/dbus.h"
|
||||||
|
#include "common/debug.h"
|
||||||
|
#include "common/edidHelper.h"
|
||||||
|
#include "common/strutil.h"
|
||||||
|
|
||||||
|
#include <string.h>
|
||||||
|
|
||||||
|
// Read the display configuration of GNOME / Mutter and Cinnamon / Muffin, including the raw EDID
|
||||||
|
// data, instead of guessing the DRM connector by matching the wl_output name (#...).
|
||||||
|
// https://gitlab.gnome.org/GNOME/mutter/-/blob/main/data/dbus-interfaces/org.gnome.Mutter.DisplayConfig.xml
|
||||||
|
|
||||||
|
// MetaMonitorColorMode
|
||||||
|
// https://gitlab.gnome.org/GNOME/mutter/-/blob/main/src/backends/meta-monitor-manager-private.h
|
||||||
|
#define FF_MUTTER_COLOR_MODE_BT2100 1
|
||||||
|
|
||||||
|
// Muffin (Cinnamon) exposes the very same interface as Mutter (GNOME), only the names differ
|
||||||
|
typedef struct FFMutterDisplayConfig {
|
||||||
|
const char* busName;
|
||||||
|
const char* objectPath;
|
||||||
|
const char* interface;
|
||||||
|
} FFMutterDisplayConfig;
|
||||||
|
|
||||||
|
static const FFMutterDisplayConfig mutterDisplayConfigs[] = {
|
||||||
|
{
|
||||||
|
.busName = "org.gnome.Mutter.DisplayConfig",
|
||||||
|
.objectPath = "/org/gnome/Mutter/DisplayConfig",
|
||||||
|
.interface = "org.gnome.Mutter.DisplayConfig",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
.busName = "org.cinnamon.Muffin.DisplayConfig",
|
||||||
|
.objectPath = "/org/cinnamon/Muffin/DisplayConfig",
|
||||||
|
.interface = "org.cinnamon.Muffin.DisplayConfig",
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
static inline DBusMessage* mutterCall(FFDBusData* dbus, const FFMutterDisplayConfig* config, const char* method) {
|
||||||
|
return ffDBusGetMethodReply(dbus, config->busName, config->objectPath, config->interface, method, nullptr, nullptr);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Returns the first display configuration interface that responds, or nullptr
|
||||||
|
static const FFMutterDisplayConfig* mutterFindConfig(FFDBusData* dbus) {
|
||||||
|
for (uint32_t i = 0; i < ARRAY_SIZE(mutterDisplayConfigs); ++i) {
|
||||||
|
DBusMessage* reply = mutterCall(dbus, &mutterDisplayConfigs[i], "GetCurrentState");
|
||||||
|
if (reply != nullptr) {
|
||||||
|
dbus->lib->ffdbus_message_unref(reply);
|
||||||
|
return &mutterDisplayConfigs[i];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return nullptr;
|
||||||
|
}
|
||||||
|
|
||||||
|
static FFDisplayResult* mutterFindDisplay(FFDisplayServerResult* result, const char* connector) {
|
||||||
|
if (connector == nullptr || *connector == '\0') {
|
||||||
|
return nullptr;
|
||||||
|
}
|
||||||
|
|
||||||
|
const uint64_t id = ffWaylandGenerateIdFromName(connector);
|
||||||
|
FF_LIST_FOR_EACH (FFDisplayResult, display, result->displays) {
|
||||||
|
if (display->id == id) {
|
||||||
|
display->platformApi = "wayland-mutter";
|
||||||
|
return display;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return nullptr;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Point `value` to the variant stored under `key` in the `a{sv}` dictionary `dict`
|
||||||
|
static bool mutterDictGet(FFDBusData* dbus, DBusMessageIter* dict, const char* key, DBusMessageIter* value) {
|
||||||
|
if (dbus->lib->ffdbus_message_iter_get_arg_type(dict) != DBUS_TYPE_ARRAY) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
DBusMessageIter entries;
|
||||||
|
dbus->lib->ffdbus_message_iter_recurse(dict, &entries);
|
||||||
|
|
||||||
|
while (dbus->lib->ffdbus_message_iter_get_arg_type(&entries) == DBUS_TYPE_DICT_ENTRY) {
|
||||||
|
DBusMessageIter entry;
|
||||||
|
dbus->lib->ffdbus_message_iter_recurse(&entries, &entry);
|
||||||
|
|
||||||
|
if (dbus->lib->ffdbus_message_iter_get_arg_type(&entry) == DBUS_TYPE_STRING) {
|
||||||
|
const char* entryKey = nullptr;
|
||||||
|
dbus->lib->ffdbus_message_iter_get_basic(&entry, &entryKey);
|
||||||
|
if (entryKey && ffStrEquals(entryKey, key) && dbus->lib->ffdbus_message_iter_next(&entry)) {
|
||||||
|
*value = entry;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!dbus->lib->ffdbus_message_iter_next(&entries)) {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Whether the `au` array contained in the variant `variant` contains `needle`
|
||||||
|
static bool mutterVariantContainsUint32(FFDBusData* dbus, DBusMessageIter* variant, uint32_t needle) {
|
||||||
|
if (dbus->lib->ffdbus_message_iter_get_arg_type(variant) != DBUS_TYPE_VARIANT) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
DBusMessageIter array;
|
||||||
|
dbus->lib->ffdbus_message_iter_recurse(variant, &array);
|
||||||
|
if (dbus->lib->ffdbus_message_iter_get_arg_type(&array) != DBUS_TYPE_ARRAY) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
DBusMessageIter items;
|
||||||
|
dbus->lib->ffdbus_message_iter_recurse(&array, &items);
|
||||||
|
|
||||||
|
while (dbus->lib->ffdbus_message_iter_get_arg_type(&items) == DBUS_TYPE_UINT32) {
|
||||||
|
uint32_t item = 0;
|
||||||
|
dbus->lib->ffdbus_message_iter_get_basic(&items, &item);
|
||||||
|
if (item == needle) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
if (!dbus->lib->ffdbus_message_iter_next(&items)) {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Copy the `ay` byte array contained in the variant `variant` into `data`
|
||||||
|
static bool mutterVariantGetBytes(FFDBusData* dbus, DBusMessageIter* variant, uint8_t* data, uint32_t capacity, uint32_t* length) {
|
||||||
|
uint32_t count = 0;
|
||||||
|
|
||||||
|
if (dbus->lib->ffdbus_message_iter_get_arg_type(variant) == DBUS_TYPE_VARIANT) {
|
||||||
|
DBusMessageIter array;
|
||||||
|
dbus->lib->ffdbus_message_iter_recurse(variant, &array);
|
||||||
|
if (dbus->lib->ffdbus_message_iter_get_arg_type(&array) == DBUS_TYPE_ARRAY) {
|
||||||
|
DBusMessageIter items;
|
||||||
|
dbus->lib->ffdbus_message_iter_recurse(&array, &items);
|
||||||
|
while (dbus->lib->ffdbus_message_iter_get_arg_type(&items) == DBUS_TYPE_BYTE) {
|
||||||
|
uint8_t byte = 0;
|
||||||
|
dbus->lib->ffdbus_message_iter_get_basic(&items, &byte);
|
||||||
|
if (count < capacity) {
|
||||||
|
data[count] = byte;
|
||||||
|
}
|
||||||
|
++count;
|
||||||
|
if (!dbus->lib->ffdbus_message_iter_next(&items)) {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
*length = count;
|
||||||
|
return count > 0 && count <= capacity;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void mutterApplyEdid(FFDisplayResult* display, const uint8_t* edid, uint32_t length) {
|
||||||
|
FF_STRBUF_AUTO_DESTROY edidName = ffStrbufCreate();
|
||||||
|
if (ffEdidGetName(edid, &edidName) && edidName.length > 0) {
|
||||||
|
ffStrbufSet(&display->name, &edidName);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (display->serial.length == 0) {
|
||||||
|
ffEdidGetSerial(edid, &display->serial);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (display->manufactureYear == 0) {
|
||||||
|
ffEdidGetManufactureDate(edid, &display->manufactureYear, &display->manufactureWeek);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (display->hdrStatus == FF_DISPLAY_HDR_STATUS_UNKNOWN) {
|
||||||
|
display->hdrStatus = ffEdidGetHdrCompatible(edid, length)
|
||||||
|
? FF_DISPLAY_HDR_STATUS_SUPPORTED
|
||||||
|
: FF_DISPLAY_HDR_STATUS_UNSUPPORTED;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (display->preferredWidth == 0) {
|
||||||
|
uint32_t width = 0, height = 0;
|
||||||
|
double refreshRate = 0;
|
||||||
|
ffEdidGetPreferredResolutionAndRefreshRate(edid, &width, &height, &refreshRate);
|
||||||
|
display->preferredWidth = width;
|
||||||
|
display->preferredHeight = height;
|
||||||
|
display->preferredRefreshRate = refreshRate;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static void mutterApplyMonitorProperties(FFDBusData* dbus, DBusMessageIter* properties, FFDisplayResult* display) {
|
||||||
|
DBusMessageIter value;
|
||||||
|
|
||||||
|
// "is-builtin" is authoritative, unlike ffdsGetDisplayType() which guesses from the connector name
|
||||||
|
if (mutterDictGet(dbus, properties, "is-builtin", &value)) {
|
||||||
|
bool builtin = false;
|
||||||
|
if (ffDBusGetBool(dbus, &value, &builtin)) {
|
||||||
|
display->type = builtin ? FF_DISPLAY_TYPE_BUILTIN : FF_DISPLAY_TYPE_EXTERNAL;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
bool hdrEnabled = false;
|
||||||
|
if (mutterDictGet(dbus, properties, "color-mode", &value)) {
|
||||||
|
uint64_t colorMode = 0;
|
||||||
|
hdrEnabled = ffDBusGetUint(dbus, &value, &colorMode) && colorMode == FF_MUTTER_COLOR_MODE_BT2100;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool hdrSupported = false;
|
||||||
|
if (mutterDictGet(dbus, properties, "supported-color-modes", &value)) {
|
||||||
|
hdrSupported = mutterVariantContainsUint32(dbus, &value, FF_MUTTER_COLOR_MODE_BT2100);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (hdrEnabled) {
|
||||||
|
display->hdrStatus = FF_DISPLAY_HDR_STATUS_ENABLED;
|
||||||
|
} else if (hdrSupported && display->hdrStatus == FF_DISPLAY_HDR_STATUS_UNKNOWN) {
|
||||||
|
display->hdrStatus = FF_DISPLAY_HDR_STATUS_SUPPORTED;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static void mutterParseCurrentState(FFDBusData* dbus, DBusMessage* reply, FFDisplayServerResult* result) {
|
||||||
|
DBusMessageIter root;
|
||||||
|
if (!dbus->lib->ffdbus_message_iter_init(reply, &root)) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// (u serial, a(monitors) monitors, a(logical_monitors) logical_monitors, a{sv} properties)
|
||||||
|
if (!dbus->lib->ffdbus_message_iter_next(&root)) { // monitors
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (dbus->lib->ffdbus_message_iter_get_arg_type(&root) == DBUS_TYPE_ARRAY) {
|
||||||
|
DBusMessageIter monitors;
|
||||||
|
dbus->lib->ffdbus_message_iter_recurse(&root, &monitors);
|
||||||
|
|
||||||
|
while (dbus->lib->ffdbus_message_iter_get_arg_type(&monitors) == DBUS_TYPE_STRUCT) {
|
||||||
|
DBusMessageIter monitor;
|
||||||
|
dbus->lib->ffdbus_message_iter_recurse(&monitors, &monitor);
|
||||||
|
|
||||||
|
// ((ssss) monitor_specs, a(siiddada{sv}) modes, a{sv} properties)
|
||||||
|
if (dbus->lib->ffdbus_message_iter_get_arg_type(&monitor) == DBUS_TYPE_STRUCT) {
|
||||||
|
DBusMessageIter specs;
|
||||||
|
dbus->lib->ffdbus_message_iter_recurse(&monitor, &specs);
|
||||||
|
|
||||||
|
const char* connector = nullptr;
|
||||||
|
if (dbus->lib->ffdbus_message_iter_get_arg_type(&specs) == DBUS_TYPE_STRING) {
|
||||||
|
dbus->lib->ffdbus_message_iter_get_basic(&specs, &connector);
|
||||||
|
}
|
||||||
|
|
||||||
|
FFDisplayResult* display = mutterFindDisplay(result, connector);
|
||||||
|
if (display) {
|
||||||
|
// (connector, vendor, product, serial)
|
||||||
|
DBusMessageIter serialSpec = specs;
|
||||||
|
if (display->serial.length == 0 &&
|
||||||
|
dbus->lib->ffdbus_message_iter_next(&serialSpec) && // vendor
|
||||||
|
dbus->lib->ffdbus_message_iter_next(&serialSpec) && // product
|
||||||
|
dbus->lib->ffdbus_message_iter_next(&serialSpec) && // serial
|
||||||
|
dbus->lib->ffdbus_message_iter_get_arg_type(&serialSpec) == DBUS_TYPE_STRING) {
|
||||||
|
const char* serial = nullptr;
|
||||||
|
dbus->lib->ffdbus_message_iter_get_basic(&serialSpec, &serial);
|
||||||
|
if (serial && !ffStrEquals(serial, "unknown")) {
|
||||||
|
ffStrbufSetS(&display->serial, serial);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Skip `modes`, then read `properties`
|
||||||
|
DBusMessageIter properties = monitor;
|
||||||
|
if (dbus->lib->ffdbus_message_iter_next(&properties) &&
|
||||||
|
dbus->lib->ffdbus_message_iter_next(&properties) &&
|
||||||
|
dbus->lib->ffdbus_message_iter_get_arg_type(&properties) == DBUS_TYPE_ARRAY) {
|
||||||
|
mutterApplyMonitorProperties(dbus, &properties, display);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
dbus->lib->ffdbus_message_iter_next(&monitors);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// logical_monitors -> primary flag
|
||||||
|
if (!dbus->lib->ffdbus_message_iter_next(&root) ||
|
||||||
|
dbus->lib->ffdbus_message_iter_get_arg_type(&root) != DBUS_TYPE_ARRAY) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
DBusMessageIter logicalMonitors;
|
||||||
|
dbus->lib->ffdbus_message_iter_recurse(&root, &logicalMonitors);
|
||||||
|
|
||||||
|
while (dbus->lib->ffdbus_message_iter_get_arg_type(&logicalMonitors) == DBUS_TYPE_STRUCT) {
|
||||||
|
DBusMessageIter logicalMonitor;
|
||||||
|
dbus->lib->ffdbus_message_iter_recurse(&logicalMonitors, &logicalMonitor);
|
||||||
|
|
||||||
|
// (i x, i y, d scale, u transform, b primary, a(ssss) monitors, a{sv} properties)
|
||||||
|
int index = 0;
|
||||||
|
bool primary = false;
|
||||||
|
|
||||||
|
do {
|
||||||
|
const int argType = dbus->lib->ffdbus_message_iter_get_arg_type(&logicalMonitor);
|
||||||
|
|
||||||
|
if (index == 4 && argType == DBUS_TYPE_BOOLEAN) {
|
||||||
|
dbus_bool_t value = 0;
|
||||||
|
dbus->lib->ffdbus_message_iter_get_basic(&logicalMonitor, &value);
|
||||||
|
primary = value != 0;
|
||||||
|
} else if (index == 5 && primary && argType == DBUS_TYPE_ARRAY) {
|
||||||
|
DBusMessageIter logicalMonitorMonitors;
|
||||||
|
dbus->lib->ffdbus_message_iter_recurse(&logicalMonitor, &logicalMonitorMonitors);
|
||||||
|
|
||||||
|
while (dbus->lib->ffdbus_message_iter_get_arg_type(&logicalMonitorMonitors) == DBUS_TYPE_STRUCT) {
|
||||||
|
DBusMessageIter specs;
|
||||||
|
dbus->lib->ffdbus_message_iter_recurse(&logicalMonitorMonitors, &specs);
|
||||||
|
if (dbus->lib->ffdbus_message_iter_get_arg_type(&specs) == DBUS_TYPE_STRING) {
|
||||||
|
const char* connector = nullptr;
|
||||||
|
dbus->lib->ffdbus_message_iter_get_basic(&specs, &connector);
|
||||||
|
FFDisplayResult* display = mutterFindDisplay(result, connector);
|
||||||
|
if (display) {
|
||||||
|
display->primary = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
dbus->lib->ffdbus_message_iter_next(&logicalMonitorMonitors);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
++index;
|
||||||
|
} while (dbus->lib->ffdbus_message_iter_next(&logicalMonitor));
|
||||||
|
|
||||||
|
dbus->lib->ffdbus_message_iter_next(&logicalMonitors);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static void mutterParseResources(FFDBusData* dbus, DBusMessage* reply, FFDisplayServerResult* result) {
|
||||||
|
DBusMessageIter root;
|
||||||
|
if (!dbus->lib->ffdbus_message_iter_init(reply, &root)) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// (u serial, a(crtcs) crtcs, a(outputs) outputs, a(modes) modes, i max_screen_width, i max_screen_height)
|
||||||
|
DBusMessageIter outputs;
|
||||||
|
if (!dbus->lib->ffdbus_message_iter_next(&root) || // crtcs
|
||||||
|
!dbus->lib->ffdbus_message_iter_next(&root) || // outputs
|
||||||
|
dbus->lib->ffdbus_message_iter_get_arg_type(&root) != DBUS_TYPE_ARRAY) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
dbus->lib->ffdbus_message_iter_recurse(&root, &outputs);
|
||||||
|
|
||||||
|
while (dbus->lib->ffdbus_message_iter_get_arg_type(&outputs) == DBUS_TYPE_STRUCT) {
|
||||||
|
DBusMessageIter output;
|
||||||
|
dbus->lib->ffdbus_message_iter_recurse(&outputs, &output);
|
||||||
|
|
||||||
|
// (u id, x output_id, i crtc_index, au crtcs, s name, au modes, au clones, a{sv} properties)
|
||||||
|
FFDisplayResult* display = nullptr;
|
||||||
|
int index = 0;
|
||||||
|
|
||||||
|
do {
|
||||||
|
const int argType = dbus->lib->ffdbus_message_iter_get_arg_type(&output);
|
||||||
|
|
||||||
|
if (index == 4 && argType == DBUS_TYPE_STRING) {
|
||||||
|
const char* connector = nullptr;
|
||||||
|
dbus->lib->ffdbus_message_iter_get_basic(&output, &connector);
|
||||||
|
display = mutterFindDisplay(result, connector);
|
||||||
|
} else if (index == 7 && display != nullptr && argType == DBUS_TYPE_ARRAY) {
|
||||||
|
DBusMessageIter value;
|
||||||
|
if (mutterDictGet(dbus, &output, "edid", &value)) {
|
||||||
|
uint8_t edid[1024];
|
||||||
|
uint32_t length = 0;
|
||||||
|
if (mutterVariantGetBytes(dbus, &value, edid, ARRAY_SIZE(edid), &length) && ffEdidIsValid(edid, length)) {
|
||||||
|
mutterApplyEdid(display, edid, length);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
++index;
|
||||||
|
} while (dbus->lib->ffdbus_message_iter_next(&output));
|
||||||
|
|
||||||
|
dbus->lib->ffdbus_message_iter_next(&outputs);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Whether org.gnome.Mutter.DisplayConfig (GNOME) or org.cinnamon.Muffin.DisplayConfig (Cinnamon)
|
||||||
|
// is reachable. Used to decide if the wl_output name based DRM connector matching
|
||||||
|
// (matchDrmConnector) can be skipped.
|
||||||
|
bool ffdsMutterAvailable(void) {
|
||||||
|
FF_DBUS_AUTO_DESTROY_DATA FFDBusData dbus = {};
|
||||||
|
if (ffDBusLoadData(DBUS_BUS_SESSION, &dbus) != nullptr) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
return mutterFindConfig(&dbus) != nullptr;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Enrich the displays detected from wl_output with the authoritative data reported by Mutter / Muffin.
|
||||||
|
// Returns true if the display configuration could be retrieved from D-Bus.
|
||||||
|
bool ffdsConnectMutter(FFDisplayServerResult* result) {
|
||||||
|
FF_DBUS_AUTO_DESTROY_DATA FFDBusData dbus = {};
|
||||||
|
const char* error = ffDBusLoadData(DBUS_BUS_SESSION, &dbus);
|
||||||
|
if (error) {
|
||||||
|
FF_DEBUG("ffDBusLoadData() failed: %s", error);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
const FFMutterDisplayConfig* config = mutterFindConfig(&dbus);
|
||||||
|
if (config == nullptr) {
|
||||||
|
FF_DEBUG("No Mutter / Muffin DisplayConfig interface found");
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
DBusMessage* reply = mutterCall(&dbus, config, "GetCurrentState");
|
||||||
|
if (reply == nullptr) {
|
||||||
|
FF_DEBUG("GetCurrentState() failed");
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
mutterParseCurrentState(&dbus, reply, result);
|
||||||
|
dbus.lib->ffdbus_message_unref(reply);
|
||||||
|
|
||||||
|
// GetCurrentState() does not expose the EDID data, so fall back to the legacy GetResources()
|
||||||
|
reply = mutterCall(&dbus, config, "GetResources");
|
||||||
|
if (reply != nullptr) {
|
||||||
|
mutterParseResources(&dbus, reply, result);
|
||||||
|
dbus.lib->ffdbus_message_unref(reply);
|
||||||
|
} else {
|
||||||
|
FF_DEBUG("GetResources() failed");
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
#endif
|
||||||
@@ -157,7 +157,7 @@ void ffWaylandOutputNameListener(void* data, [[maybe_unused]] void* output, cons
|
|||||||
|
|
||||||
display->type = ffdsGetDisplayType(name);
|
display->type = ffdsGetDisplayType(name);
|
||||||
#if __linux__
|
#if __linux__
|
||||||
if (!display->edidName.length) {
|
if (!display->edidName.length && !display->parent->useMutter) {
|
||||||
matchDrmConnector(name, display);
|
matchDrmConnector(name, display);
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
@@ -282,6 +282,14 @@ const char* ffdsConnectWayland(FFDisplayServerResult* result) {
|
|||||||
|
|
||||||
waylandDetectWM(ffwl_display_get_fd(data.display), result);
|
waylandDetectWM(ffwl_display_get_fd(data.display), result);
|
||||||
|
|
||||||
|
#if defined(FF_HAVE_DBUS) && defined(__linux__)
|
||||||
|
// gnome-shell (Mutter) and cinnamon (Muffin) expose the monitor configuration via D-Bus,
|
||||||
|
// including the raw EDID. Prefer it over matching the wl_output name against the DRM connectors.
|
||||||
|
data.useMutter =
|
||||||
|
(ffStrbufEqualS(&result->wmProcessName, "gnome-shell") || ffStrbufEqualS(&result->wmProcessName, "cinnamon")) &&
|
||||||
|
ffdsMutterAvailable();
|
||||||
|
#endif
|
||||||
|
|
||||||
struct wl_proxy* registry = ffwl_proxy_marshal_constructor((struct wl_proxy*) data.display, WL_DISPLAY_GET_REGISTRY, &wl_registry_interface, nullptr);
|
struct wl_proxy* registry = ffwl_proxy_marshal_constructor((struct wl_proxy*) data.display, WL_DISPLAY_GET_REGISTRY, &wl_registry_interface, nullptr);
|
||||||
if (registry == nullptr) {
|
if (registry == nullptr) {
|
||||||
ffwl_display_disconnect(data.display);
|
ffwl_display_disconnect(data.display);
|
||||||
@@ -309,7 +317,16 @@ const char* ffdsConnectWayland(FFDisplayServerResult* result) {
|
|||||||
data.ffwl_proxy_destroy(registry);
|
data.ffwl_proxy_destroy(registry);
|
||||||
ffwl_display_disconnect(data.display);
|
ffwl_display_disconnect(data.display);
|
||||||
|
|
||||||
{
|
bool primaryDetected = false;
|
||||||
|
#if defined(FF_HAVE_DBUS) && defined(__linux__)
|
||||||
|
if (data.useMutter) {
|
||||||
|
// Mutter / Muffin reports the primary monitor and the raw EDID directly
|
||||||
|
primaryDetected = ffdsConnectMutter(result);
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
|
// Fallback for compositors that do not expose the monitor configuration, or when it could not be read
|
||||||
|
if (!primaryDetected) {
|
||||||
const char* fileName = ffStrbufEqualS(&result->wmProcessName, "gnome-shell")
|
const char* fileName = ffStrbufEqualS(&result->wmProcessName, "gnome-shell")
|
||||||
? "monitors.xml"
|
? "monitors.xml"
|
||||||
: ffStrbufEqualS(&result->wmProcessName, "cinnamon")
|
: ffStrbufEqualS(&result->wmProcessName, "cinnamon")
|
||||||
|
|||||||
@@ -37,6 +37,7 @@ typedef struct WaylandData {
|
|||||||
WaylandProtocolType protocolType;
|
WaylandProtocolType protocolType;
|
||||||
struct wl_proxy* zxdgOutputManager;
|
struct wl_proxy* zxdgOutputManager;
|
||||||
struct wl_proxy* wpColorManager;
|
struct wl_proxy* wpColorManager;
|
||||||
|
bool useMutter; // gnome-shell: read the display configuration from org.gnome.Mutter.DisplayConfig instead of guessing it
|
||||||
} WaylandData;
|
} WaylandData;
|
||||||
|
|
||||||
typedef struct WaylandDisplay {
|
typedef struct WaylandDisplay {
|
||||||
@@ -92,4 +93,9 @@ const char* ffWaylandHandleKdeOutputOrder(WaylandData* wldata, struct wl_registr
|
|||||||
const char* ffWaylandHandleZxdgOutput(WaylandData* wldata, struct wl_registry* registry, uint32_t name, uint32_t version);
|
const char* ffWaylandHandleZxdgOutput(WaylandData* wldata, struct wl_registry* registry, uint32_t name, uint32_t version);
|
||||||
const char* ffWaylandHandleWpColor(WaylandData* wldata, struct wl_registry* registry, uint32_t name, uint32_t version);
|
const char* ffWaylandHandleWpColor(WaylandData* wldata, struct wl_registry* registry, uint32_t name, uint32_t version);
|
||||||
|
|
||||||
|
#if defined(FF_HAVE_DBUS) && defined(__linux__)
|
||||||
|
bool ffdsMutterAvailable(void);
|
||||||
|
bool ffdsConnectMutter(FFDisplayServerResult* result);
|
||||||
|
#endif
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
|||||||
@@ -272,7 +272,7 @@ static bool loadAmdGpuIdsInc(uint16_t deviceId, uint8_t revision, FFGPUResult* g
|
|||||||
|
|
||||||
static void parseAmdGpuIdsFile(uint16_t deviceId, uint8_t revision, FFGPUResult* gpu) {
|
static void parseAmdGpuIdsFile(uint16_t deviceId, uint8_t revision, FFGPUResult* gpu) {
|
||||||
char query[32];
|
char query[32];
|
||||||
snprintf(query, ARRAY_SIZE(query), "%X,\t%X,", (unsigned) deviceId, (unsigned) revision);
|
snprintf(query, ARRAY_SIZE(query), "%04X,\t%02X,", (unsigned) deviceId, (unsigned) revision);
|
||||||
#ifdef FF_CUSTOM_AMDGPU_IDS_PATH
|
#ifdef FF_CUSTOM_AMDGPU_IDS_PATH
|
||||||
FF_DEBUG("Searching AMD GPU IDs at custom path '%s' for device=0x%04x revision=0x%02x", FF_STR(FF_CUSTOM_AMDGPU_IDS_PATH), deviceId, revision);
|
FF_DEBUG("Searching AMD GPU IDs at custom path '%s' for device=0x%04x revision=0x%02x", FF_STR(FF_CUSTOM_AMDGPU_IDS_PATH), deviceId, revision);
|
||||||
ffParsePropFile(FF_STR(FF_CUSTOM_AMDGPU_IDS_PATH), query, &gpu->name);
|
ffParsePropFile(FF_STR(FF_CUSTOM_AMDGPU_IDS_PATH), query, &gpu->name);
|
||||||
|
|||||||
@@ -59,6 +59,8 @@ static pid_t getShellInfo(FFShellResult* result, pid_t pid) {
|
|||||||
ffStrbufEqualS(&result->processName, "valgrind") ||
|
ffStrbufEqualS(&result->processName, "valgrind") ||
|
||||||
ffStrbufEqualS(&result->processName, "fastfetch") || // #994
|
ffStrbufEqualS(&result->processName, "fastfetch") || // #994
|
||||||
ffStrbufEqualS(&result->processName, "flashfetch") ||
|
ffStrbufEqualS(&result->processName, "flashfetch") ||
|
||||||
|
ffStrbufEqualS(&result->processName, "mise") ||
|
||||||
|
ffStrbufEqualS(&result->processName, "mise-shim") ||
|
||||||
ffStrbufEqualS(&result->processName, "proot") ||
|
ffStrbufEqualS(&result->processName, "proot") ||
|
||||||
ffStrbufEqualS(&result->processName, "script") ||
|
ffStrbufEqualS(&result->processName, "script") ||
|
||||||
#ifdef __linux__
|
#ifdef __linux__
|
||||||
|
|||||||
@@ -32,7 +32,7 @@ static uint32_t getShellInfo(FFShellResult* result, uint32_t pid) {
|
|||||||
if (
|
if (
|
||||||
!gui && (ffStrbufIgnCaseEqualS(&result->prettyName, "sudo") || ffStrbufIgnCaseEqualS(&result->prettyName, "su") || ffStrbufIgnCaseEqualS(&result->prettyName, "gdb") || ffStrbufIgnCaseEqualS(&result->prettyName, "lldb") || ffStrbufIgnCaseEqualS(&result->prettyName, "lldb-dap") || ffStrbufIgnCaseEqualS(&result->prettyName, "python") || // python on windows generates shim executables
|
!gui && (ffStrbufIgnCaseEqualS(&result->prettyName, "sudo") || ffStrbufIgnCaseEqualS(&result->prettyName, "su") || ffStrbufIgnCaseEqualS(&result->prettyName, "gdb") || ffStrbufIgnCaseEqualS(&result->prettyName, "lldb") || ffStrbufIgnCaseEqualS(&result->prettyName, "lldb-dap") || ffStrbufIgnCaseEqualS(&result->prettyName, "python") || // python on windows generates shim executables
|
||||||
ffStrbufIgnCaseEqualS(&result->prettyName, "fastfetch") || // scoop warps the real binaries with a "shim" exe
|
ffStrbufIgnCaseEqualS(&result->prettyName, "fastfetch") || // scoop warps the real binaries with a "shim" exe
|
||||||
ffStrbufIgnCaseEqualS(&result->prettyName, "flashfetch") || ffStrbufContainIgnCaseS(&result->prettyName, "debug") || ffStrbufContainIgnCaseS(&result->prettyName, "time") || ffStrbufStartsWithIgnCaseS(&result->prettyName, "ConEmuC") // https://github.com/fastfetch-cli/fastfetch/issues/488#issuecomment-1619982014
|
ffStrbufIgnCaseEqualS(&result->prettyName, "flashfetch") || ffStrbufIgnCaseEqualS(&result->prettyName, "mise") || ffStrbufIgnCaseEqualS(&result->prettyName, "mise-shim") || ffStrbufContainIgnCaseS(&result->prettyName, "debug") || ffStrbufContainIgnCaseS(&result->prettyName, "time") || ffStrbufStartsWithIgnCaseS(&result->prettyName, "ConEmuC") // https://github.com/fastfetch-cli/fastfetch/issues/488#issuecomment-1619982014
|
||||||
)) {
|
)) {
|
||||||
ffStrbufClear(&result->processName);
|
ffStrbufClear(&result->processName);
|
||||||
ffStrbufClear(&result->prettyName);
|
ffStrbufClear(&result->prettyName);
|
||||||
|
|||||||
@@ -1,19 +1,37 @@
|
|||||||
#include "uptime.h"
|
#include "uptime.h"
|
||||||
|
#include "common/debug.h"
|
||||||
#include "common/time.h"
|
#include "common/time.h"
|
||||||
|
|
||||||
#include <utmpx.h>
|
#include <kstat.h>
|
||||||
|
|
||||||
|
static inline void kstatFreeWrap(kstat_ctl_t** pkc) {
|
||||||
|
assert(pkc);
|
||||||
|
if (*pkc) {
|
||||||
|
kstat_close(*pkc);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
const char* ffDetectUptime(FFUptimeResult* result) {
|
const char* ffDetectUptime(FFUptimeResult* result) {
|
||||||
struct utmpx* ut;
|
[[gnu::cleanup(kstatFreeWrap)]] kstat_ctl_t* kc = kstat_open();
|
||||||
|
if (!kc) {
|
||||||
|
FF_DEBUG("kstat_open() failed");
|
||||||
|
return "kstat_open() failed";
|
||||||
|
}
|
||||||
|
|
||||||
setutxent();
|
kstat_t* ks = kstat_lookup(kc, "unix", 0, "system_misc");
|
||||||
while (nullptr != (ut = getutxent())) {
|
if (!ks || kstat_read(kc, ks, nullptr) < 0) {
|
||||||
if (ut->ut_type == BOOT_TIME) {
|
FF_DEBUG("kstat_lookup(unix:0:system_misc) failed");
|
||||||
result->bootTime = (uint64_t) ut->ut_tv.tv_sec * 1000 + (uint64_t) ut->ut_tv.tv_usec / 1000000;
|
return "kstat_lookup(unix:0:system_misc) failed";
|
||||||
|
}
|
||||||
|
|
||||||
|
kstat_named_t* bootTime = (kstat_named_t*) kstat_data_lookup(ks, "boot_time");
|
||||||
|
if (!bootTime) {
|
||||||
|
FF_DEBUG("kstat_data_lookup(boot_time) failed");
|
||||||
|
return "kstat_data_lookup(boot_time) failed";
|
||||||
|
}
|
||||||
|
|
||||||
|
result->bootTime = (uint64_t) bootTime->value.ui32 * 1000;
|
||||||
result->uptime = ffTimeGetNow() - result->bootTime;
|
result->uptime = ffTimeGetNow() - result->bootTime;
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
endutxent();
|
|
||||||
return nullptr;
|
return nullptr;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -52,15 +52,15 @@ const char* ffDetectWifi(FFlist* result) {
|
|||||||
|
|
||||||
if (inf.ssid) { // https://developer.apple.com/forums/thread/732431
|
if (inf.ssid) { // https://developer.apple.com/forums/thread/732431
|
||||||
ffStrbufAppendS(&item->conn.ssid, inf.ssid.UTF8String);
|
ffStrbufAppendS(&item->conn.ssid, inf.ssid.UTF8String);
|
||||||
} else if (networkProfile.ssid) {
|
} else if (networkProfile.ssid) { // Dead in macOS 27.2
|
||||||
ffStrbufSetStatic(&item->conn.ssid, inf.configuration.networkProfiles.firstObject.ssid.UTF8String);
|
ffStrbufSetStatic(&item->conn.ssid, networkProfile.ssid.UTF8String);
|
||||||
} else {
|
} else {
|
||||||
ffStrbufSetStatic(&item->conn.ssid, "<redacted>"); // https://developer.apple.com/forums/thread/732431
|
ffStrbufSetStatic(&item->conn.ssid, "<redacted>");
|
||||||
}
|
}
|
||||||
|
|
||||||
if (inf.bssid) {
|
if (inf.bssid) {
|
||||||
ffStrbufAppendS(&item->conn.bssid, inf.bssid.UTF8String);
|
ffStrbufAppendS(&item->conn.bssid, inf.bssid.UTF8String);
|
||||||
} else if (networkProfile.bssidList) {
|
} else if (networkProfile.bssidList) { // Dead in macOS 27.2
|
||||||
ffStrbufSetStatic(&item->conn.bssid, [networkProfile.bssidList.firstObject[@"BSSID"] UTF8String]);
|
ffStrbufSetStatic(&item->conn.bssid, [networkProfile.bssidList.firstObject[@"BSSID"] UTF8String]);
|
||||||
} else {
|
} else {
|
||||||
ffStrbufSetStatic(&item->conn.bssid, "<redacted>");
|
ffStrbufSetStatic(&item->conn.bssid, "<redacted>");
|
||||||
|
|||||||
@@ -615,6 +615,17 @@ static const FFlogo A[] = {
|
|||||||
.colorTitle = FF_COLOR_FG_DEFAULT,
|
.colorTitle = FF_COLOR_FG_DEFAULT,
|
||||||
},
|
},
|
||||||
#endif
|
#endif
|
||||||
|
#ifdef FASTFETCH_DATATEXT_LOGO_ARMADA
|
||||||
|
// ArmadaOS
|
||||||
|
{
|
||||||
|
.names = { "Armada" },
|
||||||
|
.lines = FASTFETCH_DATATEXT_LOGO_ARMADA,
|
||||||
|
.colors = {
|
||||||
|
FF_COLOR_FG_DEFAULT,
|
||||||
|
FF_COLOR_FG_BLUE,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
#endif
|
||||||
#ifdef FASTFETCH_DATATEXT_LOGO_ARMBIAN
|
#ifdef FASTFETCH_DATATEXT_LOGO_ARMBIAN
|
||||||
// Armbian
|
// Armbian
|
||||||
{
|
{
|
||||||
@@ -845,6 +856,25 @@ static const FFlogo A[] = {
|
|||||||
.colorTitle = FF_COLOR_FG_DEFAULT,
|
.colorTitle = FF_COLOR_FG_DEFAULT,
|
||||||
},
|
},
|
||||||
#endif
|
#endif
|
||||||
|
#ifdef FASTFETCH_DATATEXT_LOGO_ASTROS
|
||||||
|
// AstrOS
|
||||||
|
{
|
||||||
|
.names = { "AstrOS" },
|
||||||
|
.lines = FASTFETCH_DATATEXT_LOGO_ASTROS,
|
||||||
|
.colors = {
|
||||||
|
FF_COLOR_FG_RGB "54;19;170",
|
||||||
|
FF_COLOR_FG_RGB "73;32;182",
|
||||||
|
FF_COLOR_FG_RGB "91;46;193",
|
||||||
|
FF_COLOR_FG_RGB "110;59;205",
|
||||||
|
FF_COLOR_FG_RGB "129;73;216",
|
||||||
|
FF_COLOR_FG_RGB "148;86;228",
|
||||||
|
FF_COLOR_FG_RGB "166;100;239",
|
||||||
|
FF_COLOR_FG_RGB "185;113;251",
|
||||||
|
},
|
||||||
|
.colorKeys = FF_COLOR_FG_RGB "148;86;228",
|
||||||
|
.colorTitle = FF_COLOR_FG_RGB "185;113;251",
|
||||||
|
},
|
||||||
|
#endif
|
||||||
#ifdef FASTFETCH_DATATEXT_LOGO_JANUSLINUX
|
#ifdef FASTFETCH_DATATEXT_LOGO_JANUSLINUX
|
||||||
// Ataraxia
|
// Ataraxia
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -0,0 +1,17 @@
|
|||||||
|
#
|
||||||
|
#\
|
||||||
|
/##
|
||||||
|
###
|
||||||
|
####
|
||||||
|
##### #\
|
||||||
|
###### ##
|
||||||
|
####### ##\
|
||||||
|
######## |###
|
||||||
|
########/ ####\
|
||||||
|
/######### ####|
|
||||||
|
##########/ /#####
|
||||||
|
############ #####|
|
||||||
|
#############/ ######/
|
||||||
|
##############/ #######
|
||||||
|
#######^^^^^^^^/ /######/
|
||||||
|
.#^'' `'^#'
|
||||||
@@ -0,0 +1,20 @@
|
|||||||
|
$7▁▃▅▅▅▅▃$8▁
|
||||||
|
$7▗████████▖
|
||||||
|
$6▟$7██████████▙
|
||||||
|
$6▟███████$7█████▙
|
||||||
|
$6▗████████████████$7▖
|
||||||
|
$5▗██$6████████████████▖
|
||||||
|
$5▟█████████$6███████████▙
|
||||||
|
$5▟████████▛▔▔▔▔▜██$6██████▙ ▝▀▀▀▇▇▅▃
|
||||||
|
$4▗▇███$5█████▘ ▝████████$6█▖ ▜██▙
|
||||||
|
$4▗█████████▘ $5▝█████████▖ $6▟███
|
||||||
|
$4▄█████████▘ ▐▌ $5▝█████████▙ ▟███$6▌
|
||||||
|
$3▟████$4████▛ ▁▂▃▅██▅▃▂▂ ▜█$5███████▋▁▄████▘
|
||||||
|
$3▗▟████████▛ $4▔▀██▀▔▔ ▜████▛▀▂$5▅████▀
|
||||||
|
$3▗█████████▀ ▝▘ $4▀▀▂▃▆████▛▀
|
||||||
|
$2▂ ▃██████$3███▘ ▁▂$4▄▅▆█████▀▀▂▅█▙
|
||||||
|
$2▟█████████▘▄▄▄$3▄▄▄▄▄▄▅▅▅▆▇▇██████▀▀▀▂$4▄▆█████▙
|
||||||
|
$1▟$2████████▛▘▝▀▀▀▀▀▀▀▀▀▀$3▀▀▀▀▀▀▔▔▔ ▝███████$4██▙▖
|
||||||
|
$1████████$2█▛ $3▜█████████
|
||||||
|
$1▜███████▛ $3▜███████▛
|
||||||
|
$1▔▀▀▀▀▀▔ $2▔▀$3▀▀▀▀▔
|
||||||
@@ -85,6 +85,17 @@ static const FFlogo T[] = {
|
|||||||
},
|
},
|
||||||
},
|
},
|
||||||
#endif
|
#endif
|
||||||
|
#ifdef FASTFETCH_DATATEXT_LOGO_TINYCORE
|
||||||
|
// TinyCore
|
||||||
|
{
|
||||||
|
.names = { "TinyCore" },
|
||||||
|
.lines = FASTFETCH_DATATEXT_LOGO_TINYCORE,
|
||||||
|
.colors = {
|
||||||
|
FF_COLOR_FG_BLUE,
|
||||||
|
FF_COLOR_FG_LIGHT_WHITE,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
#endif
|
||||||
#ifdef FASTFETCH_DATATEXT_LOGO_TORIZONCORE
|
#ifdef FASTFETCH_DATATEXT_LOGO_TORIZONCORE
|
||||||
// Torizon OS
|
// Torizon OS
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -0,0 +1,10 @@
|
|||||||
|
$1 .-~~~~~~~-.
|
||||||
|
$1 .' $2tiny$1 '.
|
||||||
|
$1 / / \
|
||||||
|
$1 ; / ;
|
||||||
|
$1 | / |
|
||||||
|
$1 | / |
|
||||||
|
$1 ; / $2micro$1 ;
|
||||||
|
$1 \ / /
|
||||||
|
$1 '. .'
|
||||||
|
$1 '-.......-'
|
||||||
+4
-1
@@ -143,10 +143,13 @@ static void logoLineCacheBuild(FFLogoLineCacheState* cache, const char* data, bo
|
|||||||
if (doColorReplacement && *data == '$') {
|
if (doColorReplacement && *data == '$') {
|
||||||
++data;
|
++data;
|
||||||
|
|
||||||
if (*data == '$' || *data == '\0') {
|
// `$$` is an escaped dollar; a `$` ending a line or the logo is a plain one
|
||||||
|
if (*data == '$' || *data == '\0' || *data == '\n' || *data == '\r') {
|
||||||
ffStrbufAppendC(&line, '$');
|
ffStrbufAppendC(&line, '$');
|
||||||
++lineWidth;
|
++lineWidth;
|
||||||
|
if (*data == '$') {
|
||||||
++data;
|
++data;
|
||||||
|
}
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -80,6 +80,14 @@ void ffParseSeparatorJsonObject(FFSeparatorOptions* options, yyjson_val* module)
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (unsafe_yyjson_equals_str(key, "string")) {
|
if (unsafe_yyjson_equals_str(key, "string")) {
|
||||||
|
if (!yyjson_is_str(val)) {
|
||||||
|
ffPrintError(FF_MODULE_GET_DISPLAY_NAME(Separator), 0, nullptr, FF_PRINT_TYPE_NO_CUSTOM_KEY, "Property 'string' must be a string");
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
if (unsafe_yyjson_get_len(val) == 0) {
|
||||||
|
ffPrintError(FF_MODULE_GET_DISPLAY_NAME(Separator), 0, nullptr, FF_PRINT_TYPE_NO_CUSTOM_KEY, "Property 'string' must not be empty");
|
||||||
|
continue;
|
||||||
|
}
|
||||||
ffStrbufSetJsonVal(&options->string, val);
|
ffStrbufSetJsonVal(&options->string, val);
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -60,7 +60,7 @@ void ffGenerateUptimeJsonConfig(FFUptimeOptions* options, yyjson_mut_doc* doc, y
|
|||||||
}
|
}
|
||||||
|
|
||||||
bool ffGenerateUptimeJsonResult([[maybe_unused]] FFUptimeOptions* options, yyjson_mut_doc* doc, yyjson_mut_val* module) {
|
bool ffGenerateUptimeJsonResult([[maybe_unused]] FFUptimeOptions* options, yyjson_mut_doc* doc, yyjson_mut_val* module) {
|
||||||
FFUptimeResult result;
|
FFUptimeResult result = {};
|
||||||
const char* error = ffDetectUptime(&result);
|
const char* error = ffDetectUptime(&result);
|
||||||
|
|
||||||
if (error) {
|
if (error) {
|
||||||
|
|||||||
@@ -17,6 +17,19 @@ static void verify(const char* format, const char* arg, const char* expected, in
|
|||||||
|
|
||||||
#define VERIFY(format, argument, expected) verify((format), (argument), (expected), __LINE__)
|
#define VERIFY(format, argument, expected) verify((format), (argument), (expected), __LINE__)
|
||||||
|
|
||||||
|
static void verify2(const char* format, const char* arg1, const char* arg2, const char* expected, int lineNo) {
|
||||||
|
FF_STRBUF_AUTO_DESTROY result = ffStrbufCreate();
|
||||||
|
FF_STRBUF_AUTO_DESTROY formatter = ffStrbufCreateStatic(format);
|
||||||
|
const FFformatarg arguments[] = { { .type = FF_ARG_TYPE_STRING, arg1 }, { .type = FF_ARG_TYPE_STRING, arg2 } };
|
||||||
|
ffParseFormatString(&result, &formatter, 2, arguments);
|
||||||
|
if (!ffStrbufEqualS(&result, expected)) {
|
||||||
|
fprintf(stderr, FASTFETCH_TEXT_MODIFIER_ERROR "[%d] %s: expected \"%s\", got \"%s\"\n" FASTFETCH_TEXT_MODIFIER_RESET, lineNo, format, expected, result.chars);
|
||||||
|
exit(1);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#define VERIFY2(format, argument1, argument2, expected) verify2((format), (argument1), (argument2), (expected), __LINE__)
|
||||||
|
|
||||||
int main(void) {
|
int main(void) {
|
||||||
instance.config.display.pipe = true;
|
instance.config.display.pipe = true;
|
||||||
|
|
||||||
@@ -96,9 +109,14 @@ int main(void) {
|
|||||||
VERIFY("output({1~10,10})", "12345 67890", "output()");
|
VERIFY("output({1~10,10})", "12345 67890", "output()");
|
||||||
VERIFY("output({1~10,5})", "12345 67890", "output()");
|
VERIFY("output({1~10,5})", "12345 67890", "output()");
|
||||||
VERIFY("output({1~3,-3})", "12345 67890", "output(45 67)");
|
VERIFY("output({1~3,-3})", "12345 67890", "output(45 67)");
|
||||||
|
VERIFY("output({1~0,-12})", "12345 67890", "output()");
|
||||||
VERIFY("output({1~-5,-3})", "12345 67890", "output(67)");
|
VERIFY("output({1~-5,-3})", "12345 67890", "output(67)");
|
||||||
VERIFY("output({1~-0,10})", "12345 67890", "output(12345 6789)");
|
VERIFY("output({1~-0,10})", "12345 67890", "output(12345 6789)");
|
||||||
VERIFY("output({1~-3,-5})", "12345 67890", "output()");
|
VERIFY("output({1~-3,-5})", "12345 67890", "output()");
|
||||||
|
VERIFY("output({1~11,12})", "12345 67890", "output()"); // Same as {1~11}
|
||||||
|
VERIFY("output({1~20,30})", "12345 67890", "output()"); // Same as {1~20}
|
||||||
|
VERIFY("output({1~-50,3})", "12345 67890", "output()"); // Same as {1~-50}
|
||||||
|
VERIFY("output({1~-50,-1})", "12345 67890", "output()"); // Same as {1~-50}
|
||||||
VERIFY("output({1~})", "12345 67890", "output(12345 67890)"); // Same as {1~0}
|
VERIFY("output({1~})", "12345 67890", "output(12345 67890)"); // Same as {1~0}
|
||||||
VERIFY("output({1~-0})", "12345 67890", "output(12345 67890)"); // Same as {1~0}
|
VERIFY("output({1~-0})", "12345 67890", "output(12345 67890)"); // Same as {1~0}
|
||||||
VERIFY("output({1~,-1})", "12345 67890", "output(12345 6789)"); // Same as {1~0,-1}
|
VERIFY("output({1~,-1})", "12345 67890", "output(12345 6789)"); // Same as {1~0,-1}
|
||||||
@@ -122,6 +140,28 @@ int main(void) {
|
|||||||
VERIFY("output({?1}OK{?}{/1}NOT OK{/})", "", "output(NOT OK)");
|
VERIFY("output({?1}OK{?}{/1}NOT OK{/})", "", "output(NOT OK)");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
{
|
||||||
|
// a skipped conditional may hold conditionals of its own
|
||||||
|
VERIFY2("{?1}A{?2}B{?}C{?}D", "x", "x", "ABCD");
|
||||||
|
VERIFY2("{?1}A{?2}B{?}C{?}D", "x", "", "ACD");
|
||||||
|
VERIFY2("{?1}A{?2}B{?}C{?}D", "", "x", "D");
|
||||||
|
VERIFY2("{?1}A{?2}B{?}C{?}D", "", "", "D");
|
||||||
|
VERIFY2("{/1}A{/2}B{/}C{/}D", "", "", "ABCD");
|
||||||
|
VERIFY2("{/1}A{/2}B{/}C{/}D", "", "x", "ACD");
|
||||||
|
VERIFY2("{/1}A{/2}B{/}C{/}D", "x", "", "D");
|
||||||
|
VERIFY2("{/1}A{/2}B{/}C{/}D", "x", "x", "D");
|
||||||
|
VERIFY2("{?1}A{?1}B{?1}C{?}D{?}E{?}F", "", "", "F");
|
||||||
|
VERIFY2("{?1}{/2}A{/}B{?}C", "", "", "C");
|
||||||
|
|
||||||
|
// a conditional with an invalid argument does not open a block
|
||||||
|
VERIFY2("{?1}A{?3}B{?}C", "", "x", "C");
|
||||||
|
VERIFY2("{?1}A{?3}B{?}C{?}D", "", "x", "C{?}D");
|
||||||
|
|
||||||
|
// an escaped brace does not start a placeholder
|
||||||
|
VERIFY2("{?1}{{?}}A{?}B", "", "", "B");
|
||||||
|
VERIFY2("{?1}{{?2}A{?}B", "", "x", "B");
|
||||||
|
}
|
||||||
|
|
||||||
{
|
{
|
||||||
VERIFY("output({1:3})", "\e[1m12345", "output(\e[1m123)");
|
VERIFY("output({1:3})", "\e[1m12345", "output(\e[1m123)");
|
||||||
VERIFY("output({1:-3})", "\e[1m12345", "output(\e[1m123…)");
|
VERIFY("output({1:-3})", "\e[1m12345", "output(\e[1m123…)");
|
||||||
@@ -164,6 +204,22 @@ int main(void) {
|
|||||||
VERIFY("output({1~4})", "\e[?25l12345", "output(\e[?25l5)");
|
VERIFY("output({1~4})", "\e[?25l12345", "output(\e[?25l5)");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
{
|
||||||
|
// numbers that don't fit in 32 bits must not wrap around to a valid value
|
||||||
|
VERIFY("output({4294967296})", "x", "output({4294967296})");
|
||||||
|
VERIFY("output({4294967297})", "x", "output({4294967297})");
|
||||||
|
VERIFY("output({99999999999999999999})", "x", "output({99999999999999999999})");
|
||||||
|
VERIFY("output({1~4294967297})", "12345 67890", "output()");
|
||||||
|
VERIFY("output({1~-4294967297})", "12345 67890", "output()");
|
||||||
|
VERIFY("output({1~0,4294967296})", "12345 67890", "output(12345 67890)");
|
||||||
|
VERIFY("output({1:4294967297})", "12345 67890", "output(12345 67890)");
|
||||||
|
VERIFY("output({1:-4294967296})", "12345 67890", "output(12345 67890)");
|
||||||
|
|
||||||
|
// the length is negated for the ellipsis, which overflows for the smallest int32
|
||||||
|
VERIFY("output({1:-2147483648})", "12345 67890", "output(12345 67890)");
|
||||||
|
VERIFY("output({1:-2147483647})", "12345 67890", "output(12345 67890)");
|
||||||
|
}
|
||||||
|
|
||||||
#ifndef _WIN32 // Windows doesn't have setenv
|
#ifndef _WIN32 // Windows doesn't have setenv
|
||||||
{
|
{
|
||||||
ffListInit(&instance.config.display.constants);
|
ffListInit(&instance.config.display.constants);
|
||||||
@@ -180,6 +236,10 @@ int main(void) {
|
|||||||
VERIFY("output({$FF_INVAL})", "", "output({$FF_INVAL})");
|
VERIFY("output({$FF_INVAL})", "", "output({$FF_INVAL})");
|
||||||
VERIFY("output({$9}{$0}${-9})", "", "output({$9}{$0}${-9})");
|
VERIFY("output({$9}{$0}${-9})", "", "output({$9}{$0}${-9})");
|
||||||
VERIFY("output({$1NO})", "", "output({$1NO})");
|
VERIFY("output({$1NO})", "", "output({$1NO})");
|
||||||
|
|
||||||
|
// numbers that don't fit in 32 bits must not wrap around to a valid index
|
||||||
|
VERIFY("output({$4294967297})", "", "output({$4294967297})");
|
||||||
|
VERIFY("output({$-4294967297})", "", "output({$-4294967297})");
|
||||||
ffListDestroy(&instance.config.display.constants);
|
ffListDestroy(&instance.config.display.constants);
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
|
|||||||
@@ -1,5 +1,6 @@
|
|||||||
#include "logo/logo.h"
|
#include "logo/logo.h"
|
||||||
#include "common/textModifier.h"
|
#include "common/textModifier.h"
|
||||||
|
#include "fastfetch.h"
|
||||||
|
|
||||||
#include <stdio.h>
|
#include <stdio.h>
|
||||||
#include <stdlib.h>
|
#include <stdlib.h>
|
||||||
@@ -55,6 +56,15 @@ int main(void) {
|
|||||||
VERIFY(strcmp(ffLogoPositionToString(FF_LOGO_POSITION_TOP), "top") == 0);
|
VERIFY(strcmp(ffLogoPositionToString(FF_LOGO_POSITION_TOP), "top") == 0);
|
||||||
VERIFY(strcmp(ffLogoPositionToString(FF_LOGO_POSITION_RIGHT), "right") == 0);
|
VERIFY(strcmp(ffLogoPositionToString(FF_LOGO_POSITION_RIGHT), "right") == 0);
|
||||||
|
|
||||||
|
// A `$` ending a line or the logo is a plain dollar: it keeps the line break and the terminator.
|
||||||
|
instance.config.display.pipe = true;
|
||||||
|
ffLogoPrintChars("ab$\ncd", true);
|
||||||
|
VERIFY(instance.state.logoHeight == 1 && instance.state.logoWidth == 3);
|
||||||
|
ffLogoPrintChars("ab$", true);
|
||||||
|
VERIFY(instance.state.logoHeight == 0 && instance.state.logoWidth == 3);
|
||||||
|
ffLogoPrintChars("a$1b$$c", true);
|
||||||
|
VERIFY(instance.state.logoWidth == 4);
|
||||||
|
|
||||||
puts("\033[32mAll tests passed!" FASTFETCH_TEXT_MODIFIER_RESET);
|
puts("\033[32mAll tests passed!" FASTFETCH_TEXT_MODIFIER_RESET);
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user