mirror of
https://github.com/fastfetch-cli/fastfetch.git
synced 2026-09-12 10:22:12 +02:00
Compare commits
98 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| ea44231c78 | |||
| 60af0b9e39 | |||
| e4ecb9d069 | |||
| 3c648d84db | |||
| 45db4a5c3d | |||
| caf976bec2 | |||
| b3d855355e | |||
| 0a2eb49891 | |||
| ab2efa151a | |||
| 1aee3f81c5 | |||
| 4c7031cb12 | |||
| 81c96d0f11 | |||
| 4d8680ec2d | |||
| 039eb3a38a | |||
| d07a653404 | |||
| 76c0b7e457 | |||
| 1c0e532b79 | |||
| c79a9f1bff | |||
| 759069adfa | |||
| 66ff6adfb0 | |||
| 1dba19c49f | |||
| 2afd158d96 | |||
| 36c2ce6219 | |||
| 5f6432c150 | |||
| e9f0eca3dd | |||
| 998691847e | |||
| 64e5ef0df7 | |||
| cc1e876faf | |||
| 964a5aa8b4 | |||
| ad2601f097 | |||
| 7cae63ac05 | |||
| 0d26cfb218 | |||
| 3b6855b9da | |||
| 51414c5511 | |||
| 24f8ad7673 | |||
| e404dd62c0 | |||
| 21b315d548 | |||
| 14fa052257 | |||
| 5861012d95 | |||
| 4d9fbd93d2 | |||
| 1d99bd2e08 | |||
| 52eed51eb6 | |||
| e5237c14cb | |||
| 47d8ddad35 | |||
| 37ab01d5ee | |||
| 80f7a982e0 | |||
| cff3aed49c | |||
| 572d4ec8e9 | |||
| 5bceb29c79 | |||
| 8b153833b1 | |||
| adc92b5548 | |||
| 03e949ed30 | |||
| 6accb587af | |||
| 373e041e5e | |||
| 31bdd25a26 | |||
| 703ec597fa | |||
| 3b1314102f | |||
| d349b7f7fb | |||
| dabdb104e2 | |||
| 8d5ce7bcdb | |||
| 751568e9f3 | |||
| 430fe9fe9a | |||
| 8831c07d2b | |||
| e009bb1516 | |||
| 82e2790e1b | |||
| 64182539aa | |||
| c67fd169f8 | |||
| f58d0df336 | |||
| bd0c593871 | |||
| a032d37ad4 | |||
| a4861606b7 | |||
| 88fe34102e | |||
| 5346104ffa | |||
| 12371cdc84 | |||
| 3248afe91c | |||
| 1731ab2c5f | |||
| fcc8927917 | |||
| ccf4e2d2b5 | |||
| f2998a329d | |||
| 30365e8997 | |||
| 5b66938bf4 | |||
| 80c5600a7b | |||
| 65ce2eafed | |||
| d840c7a12d | |||
| 634e27bc42 | |||
| cc18315e84 | |||
| 83e2787b24 | |||
| ef5a9384da | |||
| 2d7e6c37ff | |||
| 92e8f23261 | |||
| e09f1d3647 | |||
| 6f01e33955 | |||
| 4b813cdf59 | |||
| 942c583c89 | |||
| 24a48c1fc0 | |||
| e2e95732e8 | |||
| d647f9e3f2 | |||
| 4895c68ca9 |
@@ -7,6 +7,13 @@ assignees: ''
|
||||
|
||||
---
|
||||
|
||||
# General description of bug:
|
||||
|
||||
|
||||
# Often helpful questions:
|
||||
* Does the issue occur across multiple terminal emulators? [Y/N]
|
||||
* Does the issue occur across multiple shells? (bash, zsh, fish, etc) [Y/N]
|
||||
|
||||
Output of `fastfetch --version`:
|
||||
```
|
||||
//paste here
|
||||
@@ -27,9 +34,8 @@ Output of `fastfetch --list-features`:
|
||||
//paste here
|
||||
```
|
||||
|
||||
<!--
|
||||
If you get the following error: `Error: couldn't find config: [...]`, copy the files in [presets](../../presets/) to `/usr/share/fastfetch/presets/` or `~/.local/share/fastfetch/presets/`.
|
||||
If this isn't possible (or to much work) for you, post the output of `fastfetch --show-errors --recache --multithreading false --disable-linewrap false`.
|
||||
If this isn't possible (or too much work) for you, post the output of `fastfetch --show-errors --recache --multithreading false --disable-linewrap false`.
|
||||
-->
|
||||
|
||||
Often helpful questions:
|
||||
* Does the issue occur across multiple terminal emulators?
|
||||
* Does the issue occur across multiple shells? (bash, zsh, fish, etc)
|
||||
|
||||
@@ -0,0 +1,37 @@
|
||||
name: manage pull requests
|
||||
|
||||
on: pull_request
|
||||
|
||||
jobs:
|
||||
check:
|
||||
name: manage pull request
|
||||
runs-on: ubuntu-latest
|
||||
permissions:
|
||||
security-events: write
|
||||
steps:
|
||||
- name: checkout repository
|
||||
uses: actions/checkout@v2
|
||||
|
||||
- name: install required packages
|
||||
run: sudo apt-get update && sudo apt-get install -y libpci-dev libvulkan-dev libwayland-dev libxrandr-dev libxcb-randr0-dev libdconf-dev libdbus-1-dev libmagickcore-dev libxfconf-0-dev libsqlite3-dev
|
||||
|
||||
- name: Initialize CodeQL
|
||||
uses: github/codeql-action/init@v1
|
||||
|
||||
- name: configure project
|
||||
run: cmake -DBUILD_TESTS=On .
|
||||
|
||||
- name: build project
|
||||
run: cmake --build . -j$(nproc) --target package
|
||||
|
||||
- name: run fastfetch
|
||||
run: ./fastfetch --recache --disable-linewrap false --hide-cursor false --show-errors true
|
||||
|
||||
- name: run flashfetch
|
||||
run: ./flashfetch
|
||||
|
||||
- name: run tests
|
||||
run: ctest
|
||||
|
||||
- name: perform CodeQL analysis
|
||||
uses: github/codeql-action/analyze@v1
|
||||
+27
-68
@@ -1,101 +1,60 @@
|
||||
name: Manage pushes and pull requests
|
||||
name: manage pushes
|
||||
|
||||
on:
|
||||
push:
|
||||
branches: [ master ]
|
||||
pull_request:
|
||||
branches: [ master ]
|
||||
|
||||
env:
|
||||
BUILD_TYPE: Release
|
||||
|
||||
jobs:
|
||||
check:
|
||||
name: Build, Run and Analyze fastfetch
|
||||
manage:
|
||||
name: manage push
|
||||
runs-on: ubuntu-latest
|
||||
permissions:
|
||||
actions: read
|
||||
contents: write
|
||||
security-events: write
|
||||
strategy:
|
||||
fail-fast: false
|
||||
matrix:
|
||||
language: [ 'cpp' ]
|
||||
steps:
|
||||
- name: Checkout repository
|
||||
uses: actions/checkout@v2
|
||||
- name: checkout repository
|
||||
uses: actions/checkout@v2
|
||||
|
||||
- name: Update and install packages
|
||||
run: sudo apt-get update && sudo apt-get install -y libpci-dev libvulkan-dev libwayland-dev libxrandr-dev libxcb-randr0-dev libdconf-dev libdbus-1-dev libxfconf-0-dev librpm-dev libzstd-dev
|
||||
- name: install required packages
|
||||
run: sudo apt-get update && sudo apt-get install -y libpci-dev libvulkan-dev libwayland-dev libxrandr-dev libxcb-randr0-dev libdconf-dev libdbus-1-dev libmagickcore-dev libxfconf-0-dev libsqlite3-dev
|
||||
|
||||
- name: Initialize CodeQL
|
||||
uses: github/codeql-action/init@v1
|
||||
with:
|
||||
languages: ${{ matrix.language }}
|
||||
- name: Initialize CodeQL
|
||||
uses: github/codeql-action/init@v1
|
||||
|
||||
- name: Configure
|
||||
run: cmake -DCMAKE_BUILD_TYPE=${{env.BUILD_TYPE}} .
|
||||
- name: configure project
|
||||
# We reuse the build binary only for the releases.
|
||||
# We set tweak version to off, because it is wrong, as git tag will happen after this step. Releases don't have a tweak, so this is ok.
|
||||
run: cmake -DSET_TWEAK=Off -DBUILD_TESTS=On .
|
||||
|
||||
- name: Build
|
||||
run: cmake --build . -j$(nproc)
|
||||
- name: build project
|
||||
run: cmake --build . -j$(nproc) --target package
|
||||
|
||||
- name: Run fastfetch
|
||||
run: ./fastfetch --recache --disable-linewrap false --hide-cursor false --show-errors true
|
||||
- name: run fastfetch
|
||||
run: ./fastfetch --recache --disable-linewrap false --hide-cursor false --show-errors true
|
||||
|
||||
- name: Run flashfetch
|
||||
run: ./flashfetch
|
||||
- name: run flashfetch
|
||||
run: ./flashfetch
|
||||
|
||||
- name: Run tests
|
||||
run: ctest
|
||||
- name: run tests
|
||||
run: ctest
|
||||
|
||||
- name: Build deb package
|
||||
run: sh packaging/deb/create.sh .
|
||||
- name: perform CodeQL analysis
|
||||
uses: github/codeql-action/analyze@v1
|
||||
|
||||
- name: Perform CodeQL Analysis
|
||||
uses: github/codeql-action/analyze@v1
|
||||
|
||||
- name: Create tarball
|
||||
if: github.event_name == 'push'
|
||||
run: tar -cvf fastfetch.tar ./fastfetch -C packaging/deb ./fastfetch.deb
|
||||
|
||||
- name: Upload tarball
|
||||
if: github.event_name == 'push'
|
||||
uses: actions/upload-artifact@v3
|
||||
with:
|
||||
name: fastfetch.tar
|
||||
path: ./fastfetch.tar
|
||||
|
||||
deploy:
|
||||
name: Create GitHub release
|
||||
runs-on: ubuntu-latest
|
||||
needs: check
|
||||
if: github.event_name == 'push'
|
||||
permissions:
|
||||
contents: write
|
||||
steps:
|
||||
- name: Download tarball
|
||||
uses: actions/download-artifact@v3
|
||||
with:
|
||||
name: fastfetch.tar
|
||||
path: ./fastfetch.tar
|
||||
|
||||
- name: Extract tarball
|
||||
run: tar --overwrite -xvf ./fastfetch.tar/fastfetch.tar
|
||||
|
||||
- name: Get fastfetch version
|
||||
- name: get fastfetch version
|
||||
id: get_version_fastfetch
|
||||
run: echo "::set-output name=release::$(./fastfetch --version-raw)"
|
||||
|
||||
- name: Get latest release version
|
||||
- name: get latest release version
|
||||
id: get_version_release
|
||||
uses: pozetroninc/github-action-get-latest-release@master
|
||||
with:
|
||||
repository: ${{ github.repository }}
|
||||
|
||||
- name: Push release
|
||||
- name: create release
|
||||
if: steps.get_version_fastfetch.outputs.release != steps.get_version_release.outputs.release
|
||||
uses: ncipollo/release-action@v1
|
||||
with:
|
||||
tag: ${{ steps.get_version_fastfetch.outputs.release }}
|
||||
commit: ${{ github.sha }}
|
||||
artifacts: ./fastfetch,./fastfetch.deb
|
||||
artifacts: ./fastfetch-*.*
|
||||
|
||||
@@ -1,3 +0,0 @@
|
||||
[submodule "packaging/aur"]
|
||||
path = packaging/aur
|
||||
url = https://aur.archlinux.org/fastfetch-git.git
|
||||
+216
-67
@@ -1,15 +1,20 @@
|
||||
cmake_minimum_required(VERSION 3.1.0) # Threads::Threads
|
||||
|
||||
project(fastfetch
|
||||
VERSION 1.1.1
|
||||
VERSION 1.4.1
|
||||
LANGUAGES C
|
||||
)
|
||||
|
||||
#e.g. +1
|
||||
#This allows to track builds between versions, without GitHub creating a new release
|
||||
set(PROJECT_VERSION_EXTRA "")
|
||||
set(THREADS_PREFER_PTHREAD_FLAG ON)
|
||||
find_package(Threads REQUIRED)
|
||||
|
||||
include(GNUInstallDirs)
|
||||
find_package(PkgConfig REQUIRED)
|
||||
|
||||
# This is set to off by github actions for release builds
|
||||
OPTION(SET_TWEAK "Add tweak to project version" ON)
|
||||
|
||||
# Also create test executables
|
||||
OPTION(BUILD_TESTS "Build tests" OFF)
|
||||
|
||||
OPTION(ENABLE_LIBPCI "Enable libpci" ON)
|
||||
OPTION(ENABLE_VULKAN "Enable vulkan" ON)
|
||||
@@ -22,8 +27,11 @@ OPTION(ENABLE_GIO "Enable gio-2.0" ON)
|
||||
OPTION(ENABLE_DCONF "Enable dconf" ON)
|
||||
OPTION(ENABLE_DBUS "Enable dbus-1" ON)
|
||||
OPTION(ENABLE_XFCONF "Enable libxfconf-0" ON)
|
||||
OPTION(ENABLE_RPM "Enable rpm" ON)
|
||||
OPTION(BUILD_TESTS "Build tests" ON)
|
||||
OPTION(ENABLE_SQLITE3 "Enable sqlite3" ON)
|
||||
OPTION(ENABLE_IMAGEMAGICK7 "Enable imagemagick 7" ON)
|
||||
OPTION(ENABLE_IMAGEMAGICK6 "Enable imagemagick 6" ON)
|
||||
OPTION(ENABLE_ZLIB "Enable zlib" ON)
|
||||
OPTION(ENABLE_CHAFA "Enable chafa" ON)
|
||||
|
||||
if(NOT CMAKE_BUILD_TYPE)
|
||||
set(CMAKE_BUILD_TYPE Release)
|
||||
@@ -32,6 +40,31 @@ endif()
|
||||
set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -Wall -Wextra -Wconversion")
|
||||
set(CMAKE_EXE_LINKER_FLAGS_RELEASE "-Wl,-O3")
|
||||
|
||||
# These variables are mainly here for android, which has a different root fs structure.
|
||||
if(NOT TARGET_DIR_ROOT)
|
||||
if(NOT ANDROID)
|
||||
set(TARGET_DIR_ROOT "")
|
||||
else()
|
||||
set(TARGET_DIR_ROOT "/data/data/com.termux/files/usr")
|
||||
endif()
|
||||
endif()
|
||||
|
||||
if(NOT TARGET_DIR_USR)
|
||||
if(NOT ANDROID)
|
||||
set(TARGET_DIR_USR "${TARGET_DIR_ROOT}/usr")
|
||||
else()
|
||||
set(TARGET_DIR_USR "${TARGET_DIR_ROOT}")
|
||||
endif()
|
||||
endif()
|
||||
|
||||
if(NOT TARGET_DIR_HOME)
|
||||
if(NOT ANDROID)
|
||||
set(TARGET_DIR_HOME "${TARGET_DIR_ROOT}/home")
|
||||
else()
|
||||
set(TARGET_DIR_HOME "/data/data/com.termux/files/home")
|
||||
endif()
|
||||
endif()
|
||||
|
||||
function(fastfetch_load_text FILENAME OUTVAR)
|
||||
file(READ "${FILENAME}" TEMP)
|
||||
string(REPLACE "\n" "\\n" TEMP "${TEMP}")
|
||||
@@ -40,6 +73,17 @@ function(fastfetch_load_text FILENAME OUTVAR)
|
||||
set("${OUTVAR}" "${TEMP}" PARENT_SCOPE)
|
||||
endfunction(fastfetch_load_text)
|
||||
|
||||
# Track commits between version bumps for output in --version
|
||||
if (SET_TWEAK AND EXISTS "${CMAKE_SOURCE_DIR}/.git")
|
||||
execute_process(
|
||||
COMMAND git describe --tags
|
||||
WORKING_DIRECTORY "${CMAKE_SOURCE_DIR}"
|
||||
OUTPUT_VARIABLE PROJECT_VERSION_TWEAK
|
||||
OUTPUT_STRIP_TRAILING_WHITESPACE
|
||||
)
|
||||
string(REGEX MATCH "-[0-9]+" PROJECT_VERSION_TWEAK "${PROJECT_VERSION_TWEAK}")
|
||||
endif()
|
||||
|
||||
fastfetch_load_text(src/data/structure.txt DATATEXT_STRUCTURE)
|
||||
fastfetch_load_text(src/data/config.txt DATATEXT_CONFIG)
|
||||
fastfetch_load_text(src/data/modules.txt DATATEXT_MODULES)
|
||||
@@ -50,12 +94,7 @@ fastfetch_load_text(src/data/help_config.txt DATATEXT_HELP_CONFIG)
|
||||
|
||||
configure_file(src/fastfetch_config.h.in fastfetch_config.h)
|
||||
|
||||
set(THREADS_PREFER_PTHREAD_FLAG ON)
|
||||
find_package(Threads REQUIRED)
|
||||
|
||||
find_package(PkgConfig REQUIRED)
|
||||
|
||||
# Init CMake targets.
|
||||
# Init CMake targets
|
||||
|
||||
add_library(libfastfetch STATIC
|
||||
src/util/FFstrbuf.c
|
||||
@@ -64,13 +103,21 @@ add_library(libfastfetch STATIC
|
||||
src/common/init.c
|
||||
src/common/threading.c
|
||||
src/common/io.c
|
||||
src/common/printing.c
|
||||
src/common/caching.c
|
||||
src/common/properties.c
|
||||
src/common/font.c
|
||||
src/common/processing.c
|
||||
src/common/logo.c
|
||||
src/common/format.c
|
||||
src/common/parsing.c
|
||||
src/common/settings.c
|
||||
src/common/library.c
|
||||
src/common/networking.c
|
||||
src/logo/logo.c
|
||||
src/logo/builtin.c
|
||||
src/logo/image/image.c
|
||||
src/logo/image/im7.c
|
||||
src/logo/image/im6.c
|
||||
src/detection/os.c
|
||||
src/detection/plasma.c
|
||||
src/detection/gtk.c
|
||||
@@ -121,112 +168,168 @@ add_library(libfastfetch STATIC
|
||||
)
|
||||
|
||||
if(ENABLE_LIBPCI)
|
||||
pkg_check_modules (LIBPCI libpci)
|
||||
pkg_check_modules(LIBPCI libpci)
|
||||
if(LIBPCI_FOUND)
|
||||
target_compile_definitions(libfastfetch PRIVATE FF_HAVE_LIBPCI=1)
|
||||
else(LIBPCI_FOUND)
|
||||
else()
|
||||
message(WARNING "Package libpci not found. Building without support.")
|
||||
endif(LIBPCI_FOUND)
|
||||
endif(ENABLE_LIBPCI)
|
||||
endif()
|
||||
endif()
|
||||
|
||||
if(ENABLE_VULKAN)
|
||||
pkg_check_modules (VULKAN vulkan)
|
||||
pkg_check_modules(VULKAN vulkan)
|
||||
if(VULKAN_FOUND)
|
||||
target_compile_definitions(libfastfetch PRIVATE FF_HAVE_VULKAN=1)
|
||||
else(VULKAN_FOUND)
|
||||
else()
|
||||
message(WARNING "Package vulkan not found. Building without support.")
|
||||
endif(VULKAN_FOUND)
|
||||
endif(ENABLE_VULKAN)
|
||||
endif()
|
||||
endif()
|
||||
|
||||
if(ENABLE_WAYLAND)
|
||||
pkg_check_modules (WAYLAND wayland-client)
|
||||
if(WAYLAND_FOUND)
|
||||
target_compile_definitions(libfastfetch PRIVATE FF_HAVE_WAYLAND=1)
|
||||
else(WAYLAND_FOUND)
|
||||
else()
|
||||
message(WARNING "Package wayland-client not found. Building without support.")
|
||||
endif(WAYLAND_FOUND)
|
||||
endif(ENABLE_WAYLAND)
|
||||
endif()
|
||||
endif()
|
||||
|
||||
if(ENABLE_XCB_RANDR)
|
||||
pkg_check_modules (XCB_RANDR xcb-randr)
|
||||
pkg_check_modules(XCB_RANDR xcb-randr)
|
||||
if(XCB_RANDR_FOUND)
|
||||
target_compile_definitions(libfastfetch PRIVATE FF_HAVE_XCB_RANDR=1)
|
||||
else(XCB_RANDR_FOUND)
|
||||
else()
|
||||
message(WARNING "Package xcb-randr not found. Building without support.")
|
||||
endif(XCB_RANDR_FOUND)
|
||||
endif(ENABLE_XCB_RANDR)
|
||||
endif()
|
||||
endif()
|
||||
|
||||
if(ENABLE_XCB)
|
||||
pkg_check_modules (XCB xcb)
|
||||
if(XCB_FOUND)
|
||||
target_compile_definitions(libfastfetch PRIVATE FF_HAVE_XCB=1)
|
||||
else(XCB_FOUND)
|
||||
else()
|
||||
message(WARNING "Package xcb not found. Building without support.")
|
||||
endif(XCB_FOUND)
|
||||
endif(ENABLE_XCB)
|
||||
endif()
|
||||
endif()
|
||||
|
||||
if(ENABLE_XRANDR)
|
||||
pkg_check_modules (XRANDR xrandr)
|
||||
pkg_check_modules(XRANDR xrandr)
|
||||
if(XRANDR_FOUND)
|
||||
target_compile_definitions(libfastfetch PRIVATE FF_HAVE_XRANDR=1)
|
||||
else(XRANDR_FOUND)
|
||||
else()
|
||||
message(WARNING "Package xrandr not found. Building without support.")
|
||||
endif(XRANDR_FOUND)
|
||||
endif(ENABLE_XRANDR)
|
||||
endif()
|
||||
endif()
|
||||
|
||||
if(ENABLE_X11)
|
||||
pkg_check_modules (X11 x11)
|
||||
pkg_check_modules(X11 x11)
|
||||
if(X11_FOUND)
|
||||
target_compile_definitions(libfastfetch PRIVATE FF_HAVE_X11=1)
|
||||
else(X11_FOUND)
|
||||
else()
|
||||
message(WARNING "Package x11 not found. Building without support.")
|
||||
endif(X11_FOUND)
|
||||
endif(ENABLE_X11)
|
||||
endif()
|
||||
endif()
|
||||
|
||||
if(ENABLE_GIO)
|
||||
pkg_check_modules (GIO gio-2.0)
|
||||
pkg_check_modules(GIO gio-2.0)
|
||||
if(GIO_FOUND)
|
||||
target_compile_definitions(libfastfetch PRIVATE FF_HAVE_GIO=1)
|
||||
else(GIO_FOUND)
|
||||
else()
|
||||
message(WARNING "Package gio-2.0 not found. Building without support.")
|
||||
endif(GIO_FOUND)
|
||||
endif(ENABLE_GIO)
|
||||
endif()
|
||||
endif()
|
||||
|
||||
if(ENABLE_DCONF)
|
||||
pkg_check_modules (DCONF dconf)
|
||||
pkg_check_modules(DCONF dconf)
|
||||
if(DCONF_FOUND)
|
||||
target_compile_definitions(libfastfetch PRIVATE FF_HAVE_DCONF=1)
|
||||
else(DCONF_FOUND)
|
||||
else()
|
||||
message(WARNING "Package dconf not found. Building without support.")
|
||||
endif(DCONF_FOUND)
|
||||
endif(ENABLE_DCONF)
|
||||
endif()
|
||||
endif()
|
||||
|
||||
if(ENABLE_DBUS)
|
||||
pkg_check_modules (DBUS dbus-1)
|
||||
pkg_check_modules(DBUS dbus-1)
|
||||
if(DBUS_FOUND)
|
||||
target_compile_definitions(libfastfetch PRIVATE FF_HAVE_DBUS=1)
|
||||
else(DBUS_FOUND)
|
||||
else()
|
||||
message(WARNING "Package dbus-1 not found. Building without support.")
|
||||
endif(DBUS_FOUND)
|
||||
endif(ENABLE_DBUS)
|
||||
endif()
|
||||
endif()
|
||||
|
||||
if(ENABLE_XFCONF)
|
||||
pkg_check_modules (XFCONF libxfconf-0)
|
||||
pkg_check_modules(XFCONF libxfconf-0)
|
||||
if(XFCONF_FOUND)
|
||||
target_compile_definitions(libfastfetch PRIVATE FF_HAVE_XFCONF=1)
|
||||
else(XFCONF_FOUND)
|
||||
else()
|
||||
message(WARNING "Package libxfconf-0 not found. Building without support.")
|
||||
endif(XFCONF_FOUND)
|
||||
endif(ENABLE_XFCONF)
|
||||
endif()
|
||||
endif()
|
||||
|
||||
if(ENABLE_RPM)
|
||||
pkg_check_modules (RPM rpm)
|
||||
if(RPM_FOUND)
|
||||
target_compile_definitions(libfastfetch PRIVATE FF_HAVE_RPM=1)
|
||||
else(RPM_FOUND)
|
||||
message(WARNING "Package librpm not found. Building without support.")
|
||||
endif(RPM_FOUND)
|
||||
endif(ENABLE_RPM)
|
||||
if(ENABLE_SQLITE3)
|
||||
pkg_check_modules(SQLITE3 sqlite3)
|
||||
if(SQLITE3_FOUND)
|
||||
target_compile_definitions(libfastfetch PRIVATE FF_HAVE_SQLITE3=1)
|
||||
else()
|
||||
message(WARNING "Package sqlite3 not found. Building without support.")
|
||||
endif()
|
||||
endif()
|
||||
|
||||
if(ENABLE_IMAGEMAGICK7)
|
||||
pkg_check_modules(IMAGEMAGICK7 MagickCore-7.Q16HDRI)
|
||||
if(NOT IMAGEMAGICK7_FOUND)
|
||||
pkg_check_modules(IMAGEMAGICK7 /usr/lib/imagemagick7/pkgconfig/MagickCore-7.Q16HDRI.pc)
|
||||
endif()
|
||||
if(NOT IMAGEMAGICK7_FOUND)
|
||||
pkg_check_modules(IMAGEMAGICK7 MagickCore-7.Q16)
|
||||
endif()
|
||||
if(NOT IMAGEMAGICK7_FOUND)
|
||||
pkg_check_modules(IMAGEMAGICK7 /usr/lib/imagemagick7/pkgconfig/MagickCore-7.Q16.pc)
|
||||
endif()
|
||||
|
||||
if(IMAGEMAGICK7_FOUND)
|
||||
target_compile_definitions(libfastfetch PRIVATE FF_HAVE_IMAGEMAGICK7=1)
|
||||
else()
|
||||
message(WARNING "Package MagickCore-7.Q16HDRI or MagickCore-7.Q16 not found. Building without support.")
|
||||
endif()
|
||||
endif()
|
||||
|
||||
if(ENABLE_IMAGEMAGICK6)
|
||||
pkg_check_modules(IMAGEMAGICK6 MagickCore-6.Q16HDRI)
|
||||
if(NOT IMAGEMAGICK6_FOUND)
|
||||
pkg_check_modules(IMAGEMAGICK6 /usr/lib/imagemagick6/pkgconfig/MagickCore-6.Q16HDRI.pc)
|
||||
endif()
|
||||
if(NOT IMAGEMAGICK6_FOUND)
|
||||
pkg_check_modules(IMAGEMAGICK6 MagickCore-6.Q16)
|
||||
endif()
|
||||
if(NOT IMAGEMAGICK6_FOUND)
|
||||
pkg_check_modules(IMAGEMAGICK6 /usr/lib/imagemagick6/pkgconfig/MagickCore-6.Q16.pc)
|
||||
endif()
|
||||
|
||||
if(IMAGEMAGICK6_FOUND)
|
||||
target_compile_definitions(libfastfetch PRIVATE FF_HAVE_IMAGEMAGICK6=1)
|
||||
else()
|
||||
message(WARNING "Package MagickCore-6.Q16HDRI or MagickCore-6.Q16 not found. Building without support.")
|
||||
endif()
|
||||
endif()
|
||||
|
||||
if(ENABLE_ZLIB)
|
||||
pkg_check_modules(ZLIB zlib)
|
||||
if(ZLIB_FOUND)
|
||||
target_compile_definitions(libfastfetch PRIVATE FF_HAVE_ZLIB=1)
|
||||
else()
|
||||
message(WARNING "Package zlib not found. Building without support")
|
||||
endif()
|
||||
endif()
|
||||
|
||||
if(ENABLE_CHAFA)
|
||||
pkg_check_modules(CHAFA chafa>=1.10)
|
||||
if(CHAFA_FOUND)
|
||||
target_compile_definitions(libfastfetch PRIVATE FF_HAVE_CHAFA=1)
|
||||
else()
|
||||
message(WARNING "Package chafa>=1.10 not found. Building without support.")
|
||||
endif()
|
||||
endif()
|
||||
|
||||
target_include_directories(libfastfetch
|
||||
PUBLIC ${PROJECT_BINARY_DIR}
|
||||
@@ -243,7 +346,11 @@ target_include_directories(libfastfetch
|
||||
PRIVATE ${DCONF_INCLUDE_DIRS}
|
||||
PRIVATE ${DBUS_INCLUDE_DIRS}
|
||||
PRIVATE ${XFCONF_INCLUDE_DIRS}
|
||||
PRIVATE ${RPM_INCLUDE_DIRS}
|
||||
PRIVATE ${SQLITE3_INCLUDE_DIRS}
|
||||
PRIVATE ${IMAGEMAGICK7_INCLUDE_DIRS}
|
||||
PRIVATE ${IMAGEMAGICK6_INCLUDE_DIRS}
|
||||
PRIVATE ${ZLIB_INCLUDE_DIRS}
|
||||
PRIVATE ${CHAFA_INCLUDE_DIRS}
|
||||
)
|
||||
|
||||
target_link_libraries(libfastfetch
|
||||
@@ -267,7 +374,7 @@ target_link_libraries(flashfetch
|
||||
PRIVATE libfastfetch
|
||||
)
|
||||
|
||||
# Testing.
|
||||
# Testing
|
||||
|
||||
if (BUILD_TESTS)
|
||||
add_executable(fastfetch-test-performance
|
||||
@@ -288,13 +395,20 @@ if (BUILD_TESTS)
|
||||
add_test(NAME test-strbuf COMMAND fastfetch-test-strbuf)
|
||||
endif()
|
||||
|
||||
# Installation.
|
||||
# Installation
|
||||
|
||||
include(GNUInstallDirs)
|
||||
|
||||
install(
|
||||
TARGETS fastfetch
|
||||
DESTINATION ${CMAKE_INSTALL_BINDIR}
|
||||
)
|
||||
|
||||
install(
|
||||
TARGETS flashfetch
|
||||
DESTINATION ${CMAKE_INSTALL_BINDIR}
|
||||
)
|
||||
|
||||
install(
|
||||
FILES completions/bash
|
||||
DESTINATION ${CMAKE_INSTALL_DATAROOTDIR}/bash-completion/completions
|
||||
@@ -310,3 +424,38 @@ install(
|
||||
FILES ./LICENSE
|
||||
DESTINATION ${CMAKE_INSTALL_DATAROOTDIR}/licenses/${CMAKE_PROJECT_NAME}
|
||||
)
|
||||
|
||||
# Packaging
|
||||
|
||||
set(CPACK_GENERATOR "DEB;RPM;TGZ;ZIP")
|
||||
|
||||
set(CPACK_PACKAGE_CONTACT "Linus Dierheimer <Linus@Dierheimer.de>")
|
||||
set(CPACK_PACKAGE_DESCRIPTION_SUMMARY "Neofetch clone written in C")
|
||||
set(CPACK_PACKAGE_DESCRIPTION "\
|
||||
fastfetch is a neofetch-like tool for fetching system information and displaying them in a pretty way. \
|
||||
It is written in c to achieve much better performance.\
|
||||
")
|
||||
set(CPACK_PACKAGE_HOMEPAGE_URL "https://github.com/LinusDierheimer")
|
||||
|
||||
set(CPACK_DEBIAN_PACKAGE_SECTION, "utils")
|
||||
set(CPACK_DEBIAN_PACKAGE_PRIORITY "optional")
|
||||
set(CPACK_DEBIAN_PACKAGE_DEPENDS "libc6")
|
||||
set(CPACK_DEBIAN_PACKAGE_RECOMMENDS "\
|
||||
libpci3, \
|
||||
libvulkan1, \
|
||||
libwayland-client0, \
|
||||
libxcb-randr0, \
|
||||
xcb, \
|
||||
libxrandr2, \
|
||||
libx11-6, \
|
||||
libdconf1, \
|
||||
libglib2.0-0, \
|
||||
libdbus-1-3, \
|
||||
libxfconf-0-3, \
|
||||
libmagickcore-6.q16hdri-6, \
|
||||
zlib1g \
|
||||
")
|
||||
|
||||
set(CPACK_RPM_PACKAGE_LICENSE "MIT")
|
||||
|
||||
include(CPack)
|
||||
|
||||
@@ -12,7 +12,6 @@ Here i just add things that are easy to forget.
|
||||
- `libffprint`: contains the printing functions, logos, format etc
|
||||
- `fastfetch` and `flashfetch`: Executables, that initialize the config of libffprint. Fist one at runtime, second one at compile time
|
||||
- [ ] Support for printing images as ascii art logos (using imlib2 and libcaca probably)
|
||||
- [ ] Support for printing images in terminals that support it
|
||||
- [ ] Better OS output for all possible combinations of /etc/os-release variables.
|
||||
- [ ] Expose temperatures to CPU format string
|
||||
- [ ] Expose temperatures to GPU format string
|
||||
@@ -25,7 +24,6 @@ Here i just add things that are easy to forget.
|
||||
- [ ] Make CPU usage detection much faster and more accurate
|
||||
- [ ] Detect CPU usage in a common detection methode and expose it both to the cpuUsage module and the cpu format string
|
||||
- [ ] Per session caching
|
||||
- [ ] RPM packaging
|
||||
|
||||
### General
|
||||
- More presets
|
||||
|
||||
@@ -9,7 +9,7 @@ The output on my personal machine with default configurations looks like this:
|
||||
|
||||
With customization and speed being two competing goals, this project actually builds two executables.
|
||||
The main one being `fastfetch`, which can be very greatly configured via flags. These flags can be made persistent in `~/.config/fastfetch/config.conf`. To view the available options run `fastfetch --help`.
|
||||
The second executable being buildt is called `flashfetch`, which is configured at compile time to eliminate any possible overhead. Configuration of it can be very easily done in [`src/flashfetch.c`](src/flashfetch.c).
|
||||
The second executable being built is called `flashfetch`, which is configured at compile time to eliminate any possible overhead. Configuration of it can be very easily done in [`src/flashfetch.c`](src/flashfetch.c).
|
||||
At the moment the performance difference is measurable, but too small to be human recognizable. But the leap will get bigger with more and more options coming, and on slow machines this might actually make a difference.
|
||||
|
||||
There are some presets defined for fastfech in [`presets`](presets), you can can load them with `--load-config`. They may also serve as a good example for format arguments.
|
||||
@@ -28,9 +28,12 @@ The following libraries are used if present at runtime:
|
||||
* [`libwayland-client`](https://wayland.freedesktop.org/): Better resolution performance and output in wayland sessions. Supports different refresh rates per monitor.
|
||||
* [`libGIO`](https://developer.gnome.org/gio/unstable/): Needed for values that are only stored GSettings.
|
||||
* [`libDConf`](https://developer.gnome.org/dconf/unstable/): Needed for values that are only stored in DConf + Fallback for GSettings.
|
||||
* [`libmagickcore` (ImageMagick)](https://www.imagemagick.org/): Images in terminal using sixel or kitty graphics protocol.
|
||||
* [`libchafa`](https://github.com/hpjansson/chafa): Image output as ascii art.
|
||||
* [`libZ`](https://www.zlib.net/): Faster image output when using kitty graphics protocol.
|
||||
* [`libDBus`](https://www.freedesktop.org/wiki/Software/dbus): Needed for detecting current media player and song.
|
||||
* [`libXFConf`](https://gitlab.xfce.org/xfce/xfconf): Needed for XFWM theme and XFCE Terminal font.
|
||||
* [`librpm`](http://rpm.org/): Needed for rpm package count.
|
||||
* [`libsqlite3`](https://www.sqlite.org/index.html): Needed for rpm package count.
|
||||
|
||||
## Support status
|
||||
All categories not listed here should work without needing a specific implementation.
|
||||
@@ -42,16 +45,17 @@ Title, Separator, OS, Host, Kernel, Uptime, Processes, Packages, Shell, Resoluti
|
||||
|
||||
##### Logos
|
||||
```
|
||||
Android, Arch, Arco, Artix, CachyOS, CentOS, Debian, Endeavour, Fedora, Garuda, Gentoo, KDE Neon, Linux, Manjaro, Mint, Pop!_OS, RebornOS, Ubuntu, Void, Zorin
|
||||
Android, Arch, Arco, Artix, CachyOS, CentOS, Debian, Devuan, Deepin, Endeavour, Fedora, Garuda, Gentoo, KDE Neon, Kubuntu, Linux, Manjaro, Mint, NixOS, OpenSUSE, OpenSUSE Tumbleweed, OpenSUSE LEAP, Pop!_OS, RebornOS, RedstarOS, Rocky, Ubuntu, Void, Zorin
|
||||
```
|
||||
* Most of the logos have a small variant. Access it by appending _small to the logo name.
|
||||
* Some logos have an old variant. Access it by appending _old to the logo name.
|
||||
* Unknown/unsupported logos will be replaced with a generic linux logo when running fastfetch.
|
||||
* Get a list of all available logos with `fastfetch --print-logos`.
|
||||
* Printing images as logo is supported using Sixel or Kitty graphics protocol.
|
||||
|
||||
##### Package managers
|
||||
```
|
||||
Pacman, dpkg, rpm, emerge, xbps, Flatpak, Snap
|
||||
Pacman, dpkg, rpm, emerge, xbps, nix, Flatpak, Snap
|
||||
```
|
||||
|
||||
##### WM themes
|
||||
@@ -86,7 +90,7 @@ If pkg-config fails to find the headers for a library listed in [dependencies](#
|
||||
* [AUR](https://aur.archlinux.org/packages/fastfetch-git/): Packaged by me. Will install the fastfetch binary, bash completion and the presets. Git version
|
||||
* [Manjaro Repositories](https://gitlab.manjaro.org/packages/community/fastfetch): Packaged by a manjaro maintainer. Usually a bit outdated.
|
||||
* [Gentoo (Guru)](https://github.com/gentoo/guru/tree/master/app-misc/fastfetch): Packed by some other guy. May be out of date some times.
|
||||
* DEB: Run [`packaging/deb/create.sh`](packaging/deb/create.sh) from the root directory. You can give a custom build directory as an argument.
|
||||
* DEB / RPM: Run `cmake -B build && cmake --build build --target package` to build the packages in build.
|
||||
* Manually:
|
||||
* Build: Follow the [build instructions](#building).
|
||||
* Install: `sudo cmake --install build --prefix /usr/local`
|
||||
|
||||
+19
-7
@@ -122,8 +122,7 @@ __fastfetch_completion()
|
||||
"--recache"
|
||||
"--nocache"
|
||||
"--show-errors"
|
||||
"--color-logo"
|
||||
"--print-remaining-logo"
|
||||
"--logo-print-remaining"
|
||||
"--multithreading"
|
||||
"--allow-slow-operations"
|
||||
"--disable-linewrap"
|
||||
@@ -134,13 +133,23 @@ __fastfetch_completion()
|
||||
)
|
||||
|
||||
local FF_OPTIONS_STRING=(
|
||||
"--logo-type"
|
||||
"--logo-padding"
|
||||
"--logo-padding-left"
|
||||
"--logo-padding-right"
|
||||
"--logo-color-1"
|
||||
"--logo-color-2"
|
||||
"--logo-color-3"
|
||||
"--logo-color-4"
|
||||
"--logo-color-5"
|
||||
"--logo-color-6"
|
||||
"--logo-color-7"
|
||||
"--logo-color-8"
|
||||
"--logo-color-9"
|
||||
"-c"
|
||||
"--color"
|
||||
"--spacing"
|
||||
"-s"
|
||||
"--separator"
|
||||
"-x"
|
||||
"--offsetx"
|
||||
"-s"
|
||||
"--structure"
|
||||
"--set"
|
||||
"--os-format"
|
||||
@@ -222,7 +231,10 @@ __fastfetch_completion()
|
||||
"--lib-DConf"
|
||||
"--lib-DBus"
|
||||
"--lib-XFConf"
|
||||
"--lib-RPM"
|
||||
"--lib-sqlite3"
|
||||
"--lib-imagemagick"
|
||||
"--lib-z"
|
||||
"--lib-chafa"
|
||||
"--battery-dir"
|
||||
"--load-config"
|
||||
)
|
||||
|
||||
Submodule packaging/aur deleted from 7ac9c5d132
@@ -1,13 +0,0 @@
|
||||
Package: fastfetch
|
||||
Version: <VERSION>
|
||||
Section: utils
|
||||
Priority: extra
|
||||
Architecture: amd64
|
||||
Maintainer: Linus@Dierheimer.de
|
||||
Homepage: https://github.com/LinusDierheimer/fastfetch
|
||||
Installed-Size: 410K
|
||||
Description: A fast alternative to neofetch
|
||||
fastfetch is a neofetch-like tool for fetching system information and
|
||||
displaying them in a pretty way. It is written in c to achieve much better
|
||||
performance, in return only Linux and Android are supported. It also uses
|
||||
mechanisms like multithreading and caching to finish as fast as possible.
|
||||
@@ -1,28 +0,0 @@
|
||||
#!/bin/sh
|
||||
|
||||
# This script is intended to be run from the root of the repository.
|
||||
# If you don't want to use build/ as build directoy, or already build the elsewere, you can give the build directoy as argument.
|
||||
|
||||
BUILD_DIR="${1:-build}"
|
||||
ROOT_DIR="${2:-.}"
|
||||
PACKAGE_DIR="${BUILD_DIR}/packaging/deb/fastfetch"
|
||||
|
||||
# Create missing files
|
||||
mkdir -p "${BUILD_DIR}" || exit 1
|
||||
|
||||
if [ ! -f "${BUILD_DIR}/cmake_install.cmake" ]; then
|
||||
cmake -S "${ROOT_DIR}" -B "${BUILD_DIR}" || exit 2
|
||||
rm -f "${BUILD_DIR}/fastfetch" || exit 3 # Always rebuild the project if we reconfigured it
|
||||
fi
|
||||
|
||||
if [ ! -f "${BUILD_DIR}/fastfetch" ]; then
|
||||
cmake --build "${BUILD_DIR}" || exit 4
|
||||
fi
|
||||
|
||||
# Populate the files
|
||||
cmake --install "${BUILD_DIR}" --prefix "${PACKAGE_DIR}/usr" || exit 5
|
||||
mkdir -p "${PACKAGE_DIR}/DEBIAN" || exit 6
|
||||
sed "s/<VERSION>/$("${BUILD_DIR}/fastfetch" --version-raw)/g" "${ROOT_DIR}/packaging/deb/control-template" > "${PACKAGE_DIR}/DEBIAN/control" || exit 7
|
||||
|
||||
# Create the package
|
||||
dpkg-deb --build "${PACKAGE_DIR}" || exit 8
|
||||
@@ -0,0 +1,203 @@
|
||||
#include "fastfetch.h"
|
||||
|
||||
#define FF_CACHE_VALUE_EXTENSION "ffcv"
|
||||
#define FF_CACHE_SPLIT_EXTENSION "ffcs"
|
||||
|
||||
static void getCacheFilePath(FFinstance* instance, const char* moduleName, const char* extension, FFstrbuf* buffer)
|
||||
{
|
||||
ffStrbufAppend(buffer, &instance->state.cacheDir);
|
||||
ffStrbufAppendS(buffer, moduleName);
|
||||
|
||||
if(extension != NULL)
|
||||
{
|
||||
ffStrbufAppendC(buffer, '.');
|
||||
ffStrbufAppendS(buffer, extension);
|
||||
}
|
||||
}
|
||||
|
||||
static void readCacheFile(FFinstance* instance, const char* moduleName, const char* extension, FFstrbuf* buffer)
|
||||
{
|
||||
FFstrbuf path;
|
||||
ffStrbufInitA(&path, 64);
|
||||
getCacheFilePath(instance, moduleName, extension, &path);
|
||||
ffAppendFileContent(path.chars, buffer);
|
||||
ffStrbufDestroy(&path);
|
||||
}
|
||||
|
||||
static void writeCacheFile(FFinstance* instance, const char* moduleName, const char* extension, FFstrbuf* content)
|
||||
{
|
||||
FFstrbuf path;
|
||||
ffStrbufInitA(&path, 64);
|
||||
getCacheFilePath(instance, moduleName, extension, &path);
|
||||
ffWriteFileContent(path.chars, content);
|
||||
ffStrbufDestroy(&path);
|
||||
}
|
||||
|
||||
void ffCacheValidate(FFinstance* instance)
|
||||
{
|
||||
if(instance->config.recache)
|
||||
return;
|
||||
|
||||
FFstrbuf content;
|
||||
ffStrbufInit(&content);
|
||||
readCacheFile(instance, "cacheversion", "ffv", &content);
|
||||
|
||||
bool isSameVersion = ffStrbufCompS(&content, FASTFETCH_PROJECT_VERSION) == 0;
|
||||
ffStrbufDestroy(&content);
|
||||
if(isSameVersion)
|
||||
return;
|
||||
|
||||
instance->config.recache = true;
|
||||
|
||||
FFstrbuf version;
|
||||
ffStrbufInitA(&version, sizeof(FASTFETCH_PROJECT_VERSION));
|
||||
ffStrbufAppendS(&version, FASTFETCH_PROJECT_VERSION);
|
||||
writeCacheFile(instance, "cacheversion", "ffv", &version);
|
||||
ffStrbufDestroy(&version);
|
||||
}
|
||||
|
||||
void ffCacheOpenWrite(FFinstance* instance, const char* moduleName, FFcache* cache)
|
||||
{
|
||||
FFstrbuf cacheFileValue;
|
||||
ffStrbufInitA(&cacheFileValue, 64);
|
||||
getCacheFilePath(instance, moduleName, FF_CACHE_VALUE_EXTENSION, &cacheFileValue);
|
||||
cache->value = fopen(cacheFileValue.chars, "w");
|
||||
ffStrbufDestroy(&cacheFileValue);
|
||||
|
||||
FFstrbuf cacheFileSplit;
|
||||
ffStrbufInitA(&cacheFileSplit, 64);
|
||||
getCacheFilePath(instance, moduleName, FF_CACHE_SPLIT_EXTENSION, &cacheFileSplit);
|
||||
cache->split = fopen(cacheFileSplit.chars, "w");
|
||||
ffStrbufDestroy(&cacheFileSplit);
|
||||
}
|
||||
|
||||
void ffCacheClose(FFcache* cache)
|
||||
{
|
||||
if(cache->value != NULL)
|
||||
fclose(cache->value);
|
||||
|
||||
if(cache->split != NULL)
|
||||
fclose(cache->split);
|
||||
}
|
||||
|
||||
static bool printCachedValue(FFinstance* instance, const char* moduleName, const FFstrbuf* customKeyFormat)
|
||||
{
|
||||
FFstrbuf content;
|
||||
ffStrbufInitA(&content, 512);
|
||||
readCacheFile(instance, moduleName, FF_CACHE_VALUE_EXTENSION, &content);
|
||||
|
||||
ffStrbufTrimRight(&content, '\0'); //Strbuf always appends a '\0' at the end. We want the last null byte to be at the position of the length
|
||||
|
||||
if(content.length == 0)
|
||||
return false;
|
||||
|
||||
uint8_t moduleCounter = 1;
|
||||
|
||||
uint32_t startIndex = 0;
|
||||
while(startIndex < content.length)
|
||||
{
|
||||
uint32_t nullByteIndex = ffStrbufNextIndexC(&content, startIndex, '\0');
|
||||
uint8_t moduleIndex = (moduleCounter == 1 && nullByteIndex == content.length) ? 0 : moduleCounter;
|
||||
ffPrintLogoAndKey(instance, moduleName, moduleIndex, customKeyFormat);
|
||||
puts(content.chars + startIndex);
|
||||
startIndex = nullByteIndex + 1;
|
||||
++moduleCounter;
|
||||
}
|
||||
|
||||
ffStrbufDestroy(&content);
|
||||
|
||||
return moduleCounter > 1;
|
||||
}
|
||||
|
||||
static bool printCachedFormat(FFinstance* instance, const char* moduleName, const FFstrbuf* customKeyFormat, const FFstrbuf* formatString, uint32_t numArgs)
|
||||
{
|
||||
FFstrbuf content;
|
||||
ffStrbufInitA(&content, 512);
|
||||
readCacheFile(instance, moduleName, FF_CACHE_SPLIT_EXTENSION, &content);
|
||||
|
||||
ffStrbufTrimRight(&content, '\0'); //Strbuf always appends a '\0' at the end. We want the last null byte to be at the position of the length
|
||||
|
||||
if(content.length == 0)
|
||||
return false;
|
||||
|
||||
uint8_t moduleCounter = 1;
|
||||
|
||||
FFformatarg* arguments = calloc(numArgs, sizeof(FFformatarg));
|
||||
uint32_t argumentCounter = 0;
|
||||
|
||||
uint32_t startIndex = 0;
|
||||
while(startIndex < content.length)
|
||||
{
|
||||
arguments[argumentCounter].type = FF_FORMAT_ARG_TYPE_STRING;
|
||||
arguments[argumentCounter].value = &content.chars[startIndex];
|
||||
++argumentCounter;
|
||||
|
||||
uint32_t nullByteIndex = ffStrbufNextIndexC(&content, startIndex, '\0');
|
||||
|
||||
if(argumentCounter == numArgs)
|
||||
{
|
||||
uint8_t moduleIndex = (moduleCounter == 1 && nullByteIndex == content.length) ? 0 : moduleCounter;
|
||||
ffPrintFormatString(instance, moduleName, moduleIndex, customKeyFormat, formatString, NULL, numArgs, arguments);
|
||||
++moduleCounter;
|
||||
argumentCounter = 0;
|
||||
}
|
||||
|
||||
startIndex = nullByteIndex + 1;
|
||||
}
|
||||
|
||||
free(arguments);
|
||||
ffStrbufDestroy(&content);
|
||||
|
||||
return moduleCounter > 1;
|
||||
}
|
||||
|
||||
bool ffPrintFromCache(FFinstance* instance, const char* moduleName, const FFstrbuf* customKeyFormat, const FFstrbuf* formatString, uint32_t numArgs)
|
||||
{
|
||||
if(instance->config.recache)
|
||||
return false;
|
||||
|
||||
if(formatString == NULL || formatString->length == 0)
|
||||
return printCachedValue(instance, moduleName, customKeyFormat);
|
||||
else
|
||||
return printCachedFormat(instance, moduleName, customKeyFormat, formatString, numArgs);
|
||||
}
|
||||
|
||||
void ffPrintAndAppendToCache(FFinstance* instance, const char* moduleName, uint8_t moduleIndex, const FFstrbuf* customKeyFormat, FFcache* cache, const FFstrbuf* value, const FFstrbuf* formatString, uint32_t numArgs, const FFformatarg* arguments)
|
||||
{
|
||||
if(formatString == NULL || formatString->length == 0)
|
||||
{
|
||||
ffPrintLogoAndKey(instance, moduleName, moduleIndex, customKeyFormat);
|
||||
ffStrbufPutTo(value, stdout);
|
||||
}
|
||||
else
|
||||
{
|
||||
ffPrintFormatString(instance, moduleName, moduleIndex, customKeyFormat, formatString, NULL, numArgs, arguments);
|
||||
}
|
||||
|
||||
if(cache->value != NULL)
|
||||
{
|
||||
ffStrbufWriteTo(value, cache->value);
|
||||
fputc('\0', cache->value);
|
||||
}
|
||||
|
||||
if(cache->split == NULL)
|
||||
return;
|
||||
|
||||
for(uint32_t i = 0; i < numArgs; i++)
|
||||
{
|
||||
FFstrbuf buffer;
|
||||
ffStrbufInitA(&buffer, 64);
|
||||
ffFormatAppendFormatArg(&buffer, &arguments[i]);
|
||||
ffStrbufWriteTo(&buffer, cache->split);
|
||||
ffStrbufDestroy(&buffer);
|
||||
fputc('\0', cache->split);
|
||||
}
|
||||
}
|
||||
|
||||
void ffPrintAndWriteToCache(FFinstance* instance, const char* moduleName, const FFstrbuf* customKeyFormat, const FFstrbuf* value, const FFstrbuf* formatString, uint32_t numArgs, const FFformatarg* arguments)
|
||||
{
|
||||
FFcache cache;
|
||||
ffCacheOpenWrite(instance, moduleName, &cache);
|
||||
ffPrintAndAppendToCache(instance, moduleName, 0, customKeyFormat, &cache, value, formatString, numArgs, arguments);
|
||||
ffCacheClose(&cache);
|
||||
}
|
||||
@@ -0,0 +1,207 @@
|
||||
#include "fastfetch.h"
|
||||
|
||||
#include <string.h>
|
||||
|
||||
static void fontInit(FFfont* font)
|
||||
{
|
||||
ffStrbufInit(&font->pretty);
|
||||
ffStrbufInit(&font->name);
|
||||
ffStrbufInitA(&font->size, 4);
|
||||
ffListInitA(&font->styles, sizeof(FFstrbuf), 4);
|
||||
}
|
||||
|
||||
static void fontInitPretty(FFfont* font)
|
||||
{
|
||||
ffStrbufAppend(&font->pretty, &font->name);
|
||||
|
||||
if(font->size.length == 0 && font->styles.length == 0)
|
||||
return;
|
||||
else if(font->pretty.length == 0)
|
||||
ffStrbufAppendS(&font->pretty, "default");
|
||||
|
||||
ffStrbufAppendS(&font->pretty, " (");
|
||||
|
||||
if(font->size.length > 0)
|
||||
{
|
||||
ffStrbufAppend(&font->pretty, &font->size);
|
||||
ffStrbufAppendS(&font->pretty, "pt");
|
||||
|
||||
if(font->styles.length > 0)
|
||||
ffStrbufAppendS(&font->pretty, ", ");
|
||||
}
|
||||
|
||||
for(uint32_t i = 0; i < font->styles.length; i++)
|
||||
{
|
||||
ffStrbufAppend(&font->pretty, ffListGet(&font->styles, i));
|
||||
|
||||
if(i < font->styles.length - 1)
|
||||
ffStrbufAppendS(&font->pretty, ", ");
|
||||
}
|
||||
|
||||
ffStrbufAppendC(&font->pretty, ')');
|
||||
}
|
||||
|
||||
void ffFontInitQt(FFfont* font, const char* data)
|
||||
{
|
||||
fontInit(font);
|
||||
|
||||
//See https://doc.qt.io/qt-5/qfont.html#toString
|
||||
|
||||
//Family
|
||||
while(*data != ',' && *data != '\0')
|
||||
{
|
||||
ffStrbufAppendC(&font->name, *data);
|
||||
++data;
|
||||
}
|
||||
if(*data != '\0')
|
||||
++data;
|
||||
ffStrbufTrim(&font->name, ' ');
|
||||
|
||||
//Size
|
||||
while(*data != ',' && *data != '\0')
|
||||
{
|
||||
ffStrbufAppendC(&font->size, *data);
|
||||
++data;
|
||||
}
|
||||
if(*data != '\0')
|
||||
++data;
|
||||
ffStrbufTrim(&font->size, ' ');
|
||||
|
||||
#define FF_FONT_QT_SKIP_VALUE \
|
||||
while(*data != ',' && *data != '\0') \
|
||||
++data; \
|
||||
if(*data != '\0') \
|
||||
++data;
|
||||
|
||||
FF_FONT_QT_SKIP_VALUE //Pixel size
|
||||
FF_FONT_QT_SKIP_VALUE //Style hint
|
||||
FF_FONT_QT_SKIP_VALUE //Font weight
|
||||
FF_FONT_QT_SKIP_VALUE //Font style
|
||||
FF_FONT_QT_SKIP_VALUE //Underline
|
||||
FF_FONT_QT_SKIP_VALUE //Strike out
|
||||
FF_FONT_QT_SKIP_VALUE //Fixed pitch
|
||||
FF_FONT_QT_SKIP_VALUE //Always 0
|
||||
|
||||
#undef FF_FONT_QT_SKIP_VALUE
|
||||
|
||||
while(*data != '\0')
|
||||
{
|
||||
while(*data == ' ')
|
||||
++data;
|
||||
|
||||
if(*data == '\0')
|
||||
break;
|
||||
|
||||
FFstrbuf* style = ffListAdd(&font->styles);
|
||||
ffStrbufInit(style);
|
||||
while(*data != ' ' && *data != '\0')
|
||||
{
|
||||
ffStrbufAppendC(style, *data);
|
||||
++data;
|
||||
}
|
||||
}
|
||||
|
||||
fontInitPretty(font);
|
||||
}
|
||||
|
||||
static void fontPangoParseWord(const char** data, FFfont* font, FFstrbuf* alternativeBuffer)
|
||||
{
|
||||
while(**data == ' ' || **data == '\t' || **data == ',')
|
||||
++(*data);
|
||||
|
||||
const char* wordStart = *data;
|
||||
|
||||
while(**data != ' ' && **data != '\t' && **data != ',' && **data != '\0' && **data != '`' && **data != '\\')
|
||||
++(*data);
|
||||
|
||||
uint32_t wordLength = (uint32_t) (*data - wordStart);
|
||||
if(wordLength == 0)
|
||||
return;
|
||||
|
||||
if(**data == '\0' || **data == '`' || **data == '\\')
|
||||
{
|
||||
ffStrbufAppendNS(&font->size, wordLength, wordStart);
|
||||
if(ffStrbufEndsWithS(&font->size, "px"))
|
||||
ffStrbufSubstrBefore(&font->size, font->size.length - 2);
|
||||
|
||||
double dummy;
|
||||
if(sscanf(font->size.chars, "%lf", &dummy) == 1)
|
||||
return;
|
||||
|
||||
ffStrbufClear(&font->size);
|
||||
}
|
||||
|
||||
if(
|
||||
strncasecmp(wordStart, "Ultra", 5) == 0 ||
|
||||
strncasecmp(wordStart, "Extra", 5) == 0 ||
|
||||
strncasecmp(wordStart, "Semi", 4) == 0 ||
|
||||
strncasecmp(wordStart, "Demi", 4) == 0 ||
|
||||
strncasecmp(wordStart, "Normal", wordLength) == 0 ||
|
||||
strncasecmp(wordStart, "Roman", wordLength) == 0 ||
|
||||
strncasecmp(wordStart, "Oblique", wordLength) == 0 ||
|
||||
strncasecmp(wordStart, "Italic", wordLength) == 0 ||
|
||||
strncasecmp(wordStart, "Thin", wordLength) == 0 ||
|
||||
strncasecmp(wordStart, "Light", wordLength) == 0 ||
|
||||
strncasecmp(wordStart, "Bold", wordLength) == 0 ||
|
||||
strncasecmp(wordStart, "Black", wordLength) == 0 ||
|
||||
strncasecmp(wordStart, "Condensed", wordLength) == 0 ||
|
||||
strncasecmp(wordStart, "Expanded", wordLength) == 0
|
||||
) {
|
||||
if(alternativeBuffer == NULL)
|
||||
{
|
||||
alternativeBuffer = ffListAdd(&font->styles);
|
||||
ffStrbufInit(alternativeBuffer);
|
||||
}
|
||||
|
||||
ffStrbufAppendNSExludingC(alternativeBuffer, wordLength, wordStart, '-');
|
||||
|
||||
if(
|
||||
strncasecmp(wordStart, "Ultra ", 6) == 0 ||
|
||||
strncasecmp(wordStart, "Extra ", 6) == 0 ||
|
||||
strncasecmp(wordStart, "Semi ", 5) == 0 ||
|
||||
strncasecmp(wordStart, "Demi ", 5) == 0
|
||||
) {
|
||||
fontPangoParseWord(data, font, alternativeBuffer);
|
||||
}
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
if(alternativeBuffer != NULL)
|
||||
{
|
||||
ffStrbufAppendNSExludingC(alternativeBuffer, wordLength, wordStart, '-');
|
||||
return;
|
||||
}
|
||||
|
||||
if(font->name.length > 0)
|
||||
ffStrbufAppendC(&font->name, ' ');
|
||||
ffStrbufAppendNS(&font->name, wordLength, wordStart);
|
||||
}
|
||||
|
||||
void ffFontInitPango(FFfont* font, const char* data)
|
||||
{
|
||||
fontInit(font);
|
||||
|
||||
while(*data != '\0' && *data != '`' && *data != '\\')
|
||||
fontPangoParseWord(&data, font, NULL);
|
||||
|
||||
fontInitPretty(font);
|
||||
}
|
||||
|
||||
void ffFontInitCopy(FFfont* font, const char* name)
|
||||
{
|
||||
fontInit(font);
|
||||
ffStrbufAppendS(&font->name, name);
|
||||
fontInitPretty(font);
|
||||
}
|
||||
|
||||
void ffFontDestroy(FFfont* font)
|
||||
{
|
||||
ffStrbufDestroy(&font->pretty);
|
||||
ffStrbufDestroy(&font->name);
|
||||
ffStrbufDestroy(&font->size);
|
||||
|
||||
for(uint32_t i = 0; i < font->styles.length; i++)
|
||||
ffStrbufDestroy(ffListGet(&font->styles, i));
|
||||
ffListDestroy(&font->styles);
|
||||
}
|
||||
+2
-3
@@ -173,7 +173,7 @@ void ffParseFormatString(FFstrbuf* buffer, const FFstrbuf* formatstr, const FFst
|
||||
appendInvalidPlaceholder(buffer, "{", &placeholderValue, i, formatstr->length);
|
||||
else
|
||||
{
|
||||
ffStrbufAppendS(buffer, "\033[0m");
|
||||
ffStrbufAppendS(buffer, FASTFETCH_TEXT_MODIFIER_RESET);
|
||||
--numOpenColors;
|
||||
}
|
||||
|
||||
@@ -284,6 +284,5 @@ void ffParseFormatString(FFstrbuf* buffer, const FFstrbuf* formatstr, const FFst
|
||||
|
||||
ffStrbufTrimRight(buffer, ' ');
|
||||
|
||||
if(numOpenColors > 0)
|
||||
ffStrbufAppendS(buffer, "\033[0m");
|
||||
ffStrbufAppendS(buffer, FASTFETCH_TEXT_MODIFIER_RESET);
|
||||
}
|
||||
|
||||
+42
-18
@@ -58,12 +58,12 @@ static void initConfigDirs(FFstate* state)
|
||||
|
||||
FFstrbuf* systemConfigHome = ffListAdd(&state->configDirs);
|
||||
ffStrbufInitA(systemConfigHome, 64);
|
||||
ffStrbufAppendS(systemConfigHome, "/etc/xdg");
|
||||
ffStrbufAppendS(systemConfigHome, FASTFETCH_TARGET_DIR_ROOT"/etc/xdg");
|
||||
FF_ENSURE_ONLY_ONCE_IN_LIST(systemConfigHome)
|
||||
|
||||
FFstrbuf* systemConfig = ffListAdd(&state->configDirs);
|
||||
ffStrbufInitA(systemConfig, 64);
|
||||
ffStrbufAppendS(systemConfig, "/etc");
|
||||
ffStrbufAppendS(systemConfig, FASTFETCH_TARGET_DIR_ROOT"/etc");
|
||||
FF_ENSURE_ONLY_ONCE_IN_LIST(systemConfig)
|
||||
|
||||
#undef FF_ENSURE_ONLY_ONCE_IN_LIST
|
||||
@@ -92,6 +92,8 @@ static void initCacheDir(FFstate* state)
|
||||
static void initState(FFstate* state)
|
||||
{
|
||||
state->logoWidth = 0;
|
||||
state->logoHeight = 0;
|
||||
state->keysHeight = 0;
|
||||
state->passwd = getpwuid(getuid());
|
||||
uname(&state->utsname);
|
||||
sysinfo(&state->sysinfo);
|
||||
@@ -102,25 +104,26 @@ static void initState(FFstate* state)
|
||||
|
||||
static void defaultConfig(FFinstance* instance)
|
||||
{
|
||||
ffStrbufInit(&instance->config.color);
|
||||
ffStrbufInit(&instance->config.logoSource);
|
||||
instance->config.logoType = FF_LOGO_TYPE_AUTO;
|
||||
for(uint8_t i = 0; i < (uint8_t) FASTFETCH_LOGO_MAX_COLORS; ++i)
|
||||
ffStrbufInit(&instance->config.logoColors[i]);
|
||||
instance->config.logoWidth = 0;
|
||||
instance->config.logoHeight = 0; //preserve aspect ratio
|
||||
instance->config.logoPaddingLeft = 0;
|
||||
instance->config.logoPaddingRight = 4;
|
||||
instance->config.logoPrintRemaining = true;
|
||||
|
||||
ffStrbufInit(&instance->config.mainColor);
|
||||
ffStrbufInit(&instance->config.separator);
|
||||
ffStrbufAppendS(&instance->config.separator, ": ");
|
||||
|
||||
instance->config.logoKeySpacing = 4;
|
||||
instance->config.offsetx = 0;
|
||||
instance->config.colorLogo = true;
|
||||
instance->config.showErrors = false;
|
||||
instance->config.recache = false;
|
||||
instance->config.cacheSave = true;
|
||||
instance->config.printRemainingLogo = true;
|
||||
instance->config.allowSlowOperations = false;
|
||||
instance->config.disableLinewrap = true;
|
||||
instance->config.hideCursor = true;
|
||||
instance->config.userLogoIsRaw = false;
|
||||
|
||||
for(uint8_t i = 0; i < (uint8_t) FASTFETCH_LOGO_MAX_COLORS; ++i)
|
||||
ffStrbufInit(&instance->config.logoColors[i]);
|
||||
|
||||
ffStrbufInitA(&instance->config.osFormat, 0);
|
||||
ffStrbufInitA(&instance->config.osKey, 0);
|
||||
@@ -196,7 +199,10 @@ static void defaultConfig(FFinstance* instance)
|
||||
ffStrbufInitA(&instance->config.libDConf, 0);
|
||||
ffStrbufInitA(&instance->config.libDBus, 0);
|
||||
ffStrbufInitA(&instance->config.libXFConf, 0);
|
||||
ffStrbufInitA(&instance->config.librpm, 0);
|
||||
ffStrbufInitA(&instance->config.libSQLite3, 0);
|
||||
ffStrbufInitA(&instance->config.libImageMagick, 0);
|
||||
ffStrbufInitA(&instance->config.libZ, 0);
|
||||
ffStrbufInitA(&instance->config.libChafa, 0);
|
||||
|
||||
ffStrbufInitA(&instance->config.diskFolders, 0);
|
||||
|
||||
@@ -219,7 +225,6 @@ void ffInitInstance(FFinstance* instance)
|
||||
{
|
||||
initState(&instance->state);
|
||||
defaultConfig(instance);
|
||||
ffCacheValidate(instance);
|
||||
}
|
||||
|
||||
static void resetConsole(bool disableLinewrap, bool hideCursor)
|
||||
@@ -253,21 +258,28 @@ void ffStart(FFinstance* instance)
|
||||
sigaction(SIGTERM, &action, NULL);
|
||||
sigaction(SIGQUIT, &action, NULL);
|
||||
|
||||
//We do the cache validation here, so we can skip it if --recache is given
|
||||
ffCacheValidate(instance);
|
||||
|
||||
//reset everything to default before we start printing
|
||||
fputs(FASTFETCH_TEXT_MODIFIER_RESET, stdout);
|
||||
|
||||
if(instance->config.hideCursor)
|
||||
fputs("\033[?25l", stdout);
|
||||
|
||||
if(instance->config.disableLinewrap)
|
||||
fputs("\033[?7l", stdout);
|
||||
|
||||
ffPrintLogo(instance);
|
||||
}
|
||||
|
||||
void ffFinish(FFinstance* instance)
|
||||
{
|
||||
if(instance->config.printRemainingLogo)
|
||||
ffPrintRemainingLogo(instance);
|
||||
|
||||
ffPrintRemainingLogo(instance);
|
||||
resetConsole(instance->config.disableLinewrap, instance->config.hideCursor);
|
||||
}
|
||||
|
||||
//Must be in a file compiled with the libfastfetch target, because the FF_HAVE* macros are not defined for the executable targets
|
||||
void ffListFeatures()
|
||||
{
|
||||
fputs(
|
||||
@@ -301,11 +313,23 @@ void ffListFeatures()
|
||||
#ifdef FF_HAVE_DBUS
|
||||
"dbus\n"
|
||||
#endif
|
||||
#ifdef FF_HAVE_IMAGEMAGICK7
|
||||
"imagemagick7\n"
|
||||
#endif
|
||||
#ifdef FF_HAVE_IMAGEMAGICK6
|
||||
"imagemagick6\n"
|
||||
#endif
|
||||
#ifdef FF_HAVE_CHAFA
|
||||
"chafa\n"
|
||||
#endif
|
||||
#ifdef FF_HAVE_ZLIB
|
||||
"zlib\n"
|
||||
#endif
|
||||
#ifdef FF_HAVE_XFCONF
|
||||
"xfconf\n"
|
||||
#endif
|
||||
#ifdef FF_HAVE_RPM
|
||||
"librpm\n"
|
||||
#ifdef FF_HAVE_SQLITE3
|
||||
"sqlite3\n"
|
||||
#endif
|
||||
""
|
||||
, stdout);
|
||||
|
||||
+77
-411
@@ -1,421 +1,52 @@
|
||||
#include "fastfetch.h"
|
||||
|
||||
#include <malloc.h>
|
||||
#include <stdio.h>
|
||||
#include <unistd.h>
|
||||
#include <fcntl.h>
|
||||
|
||||
#define FF_IO_CACHE_VALUE_EXTENSION "ffcv"
|
||||
#define FF_IO_CACHE_SPLIT_EXTENSION "ffcs"
|
||||
|
||||
void ffPrintLogoAndKey(FFinstance* instance, const char* moduleName, uint8_t moduleIndex, const FFstrbuf* customKeyFormat)
|
||||
{
|
||||
ffPrintLogoLine(instance);
|
||||
|
||||
fputs(FASTFETCH_TEXT_MODIFIER_BOLT, stdout);
|
||||
ffPrintColor(&instance->config.color);
|
||||
|
||||
if(customKeyFormat == NULL || customKeyFormat->length == 0)
|
||||
{
|
||||
fputs(moduleName, stdout);
|
||||
|
||||
if(moduleIndex > 0)
|
||||
printf(" %hhu", moduleIndex);
|
||||
}
|
||||
else
|
||||
{
|
||||
FFstrbuf key;
|
||||
ffStrbufInit(&key);
|
||||
ffParseFormatString(&key, customKeyFormat, NULL, 1, (FFformatarg[]){
|
||||
{FF_FORMAT_ARG_TYPE_UINT8, &moduleIndex}
|
||||
});
|
||||
ffStrbufWriteTo(&key, stdout);
|
||||
ffStrbufDestroy(&key);
|
||||
}
|
||||
|
||||
fputs(FASTFETCH_TEXT_MODIFIER_RESET, stdout);
|
||||
ffStrbufWriteTo(&instance->config.separator, stdout);
|
||||
}
|
||||
|
||||
void ffPrintError(FFinstance* instance, const char* moduleName, uint8_t moduleIndex, const FFstrbuf* customKeyFormat, const FFstrbuf* formatString, uint32_t numFormatArgs, const char* message, ...)
|
||||
{
|
||||
if(!instance->config.showErrors)
|
||||
return;
|
||||
|
||||
va_list arguments;
|
||||
va_start(arguments, message);
|
||||
|
||||
if(formatString == NULL || formatString->length == 0)
|
||||
{
|
||||
ffPrintLogoAndKey(instance, moduleName, moduleIndex, customKeyFormat);
|
||||
fputs(FASTFETCH_TEXT_MODIFIER_ERROR, stdout);
|
||||
vprintf(message, arguments);
|
||||
puts(FASTFETCH_TEXT_MODIFIER_RESET);
|
||||
}
|
||||
else
|
||||
{
|
||||
FF_STRBUF_CREATE(error);
|
||||
ffStrbufAppendVF(&error, message, arguments);
|
||||
|
||||
// calloc sets all to 0 and FF_FORMAT_ARG_TYPE_NULL also has value 0 so we don't need to explictly set it
|
||||
FFformatarg* nullArgs = calloc(numFormatArgs, sizeof(FFformatarg));
|
||||
|
||||
ffPrintFormatString(instance, moduleName, moduleIndex, customKeyFormat, formatString, &error, numFormatArgs, nullArgs);
|
||||
|
||||
free(nullArgs);
|
||||
ffStrbufDestroy(&error);
|
||||
}
|
||||
|
||||
va_end(arguments);
|
||||
}
|
||||
|
||||
void ffPrintFormatString(FFinstance* instance, const char* moduleName, uint8_t moduleIndex, const FFstrbuf* customKeyFormat, const FFstrbuf* formatString, const FFstrbuf* error, uint32_t numArgs, const FFformatarg* arguments)
|
||||
{
|
||||
FFstrbuf buffer;
|
||||
ffStrbufInitA(&buffer, 256);
|
||||
|
||||
ffParseFormatString(&buffer, formatString, error, numArgs, arguments);
|
||||
|
||||
if(buffer.length > 0)
|
||||
{
|
||||
ffPrintLogoAndKey(instance, moduleName, moduleIndex, customKeyFormat);
|
||||
ffStrbufPutTo(&buffer, stdout);
|
||||
}
|
||||
|
||||
ffStrbufDestroy(&buffer);
|
||||
}
|
||||
|
||||
void ffGetCacheFilePath(FFinstance* instance, const char* moduleName, const char* extension, FFstrbuf* buffer)
|
||||
{
|
||||
ffStrbufAppend(buffer, &instance->state.cacheDir);
|
||||
ffStrbufAppendS(buffer, moduleName);
|
||||
|
||||
if(extension != NULL)
|
||||
{
|
||||
ffStrbufAppendC(buffer, '.');
|
||||
ffStrbufAppendS(buffer, extension);
|
||||
}
|
||||
}
|
||||
|
||||
void ffReadCacheFile(FFinstance* instance, const char* moduleName, const char* extension, FFstrbuf* buffer)
|
||||
{
|
||||
FFstrbuf path;
|
||||
ffStrbufInitA(&path, 64);
|
||||
ffGetCacheFilePath(instance, moduleName, extension, &path);
|
||||
ffAppendFileContent(path.chars, buffer);
|
||||
ffStrbufDestroy(&path);
|
||||
}
|
||||
|
||||
void ffWriteCacheFile(FFinstance* instance, const char* moduleName, const char* extension, FFstrbuf* content)
|
||||
{
|
||||
FFstrbuf path;
|
||||
ffStrbufInitA(&path, 64);
|
||||
ffGetCacheFilePath(instance, moduleName, extension, &path);
|
||||
ffWriteFileContent(path.chars, content);
|
||||
ffStrbufDestroy(&path);
|
||||
}
|
||||
|
||||
static bool printCachedValue(FFinstance* instance, const char* moduleName, const FFstrbuf* customKeyFormat)
|
||||
{
|
||||
FFstrbuf content;
|
||||
ffStrbufInitA(&content, 512);
|
||||
ffReadCacheFile(instance, moduleName, FF_IO_CACHE_VALUE_EXTENSION, &content);
|
||||
|
||||
ffStrbufTrimRight(&content, '\0'); //Strbuf always appends a '\0' at the end. We want the last null byte to be at the position of the length
|
||||
|
||||
if(content.length == 0)
|
||||
return false;
|
||||
|
||||
uint8_t moduleCounter = 1;
|
||||
|
||||
uint32_t startIndex = 0;
|
||||
while(startIndex < content.length)
|
||||
{
|
||||
uint32_t nullByteIndex = ffStrbufNextIndexC(&content, startIndex, '\0');
|
||||
uint8_t moduleIndex = (moduleCounter == 1 && nullByteIndex == content.length) ? 0 : moduleCounter;
|
||||
ffPrintLogoAndKey(instance, moduleName, moduleIndex, customKeyFormat);
|
||||
puts(content.chars + startIndex);
|
||||
startIndex = nullByteIndex + 1;
|
||||
++moduleCounter;
|
||||
}
|
||||
|
||||
ffStrbufDestroy(&content);
|
||||
|
||||
return moduleCounter > 1;
|
||||
}
|
||||
|
||||
static bool printCachedFormat(FFinstance* instance, const char* moduleName, const FFstrbuf* customKeyFormat, const FFstrbuf* formatString, uint32_t numArgs)
|
||||
{
|
||||
FFstrbuf content;
|
||||
ffStrbufInitA(&content, 512);
|
||||
ffReadCacheFile(instance, moduleName, FF_IO_CACHE_SPLIT_EXTENSION, &content);
|
||||
|
||||
ffStrbufTrimRight(&content, '\0'); //Strbuf always appends a '\0' at the end. We want the last null byte to be at the position of the length
|
||||
|
||||
if(content.length == 0)
|
||||
return false;
|
||||
|
||||
uint8_t moduleCounter = 1;
|
||||
|
||||
FFformatarg* arguments = calloc(numArgs, sizeof(FFformatarg));
|
||||
uint32_t argumentCounter = 0;
|
||||
|
||||
uint32_t startIndex = 0;
|
||||
while(startIndex < content.length)
|
||||
{
|
||||
arguments[argumentCounter].type = FF_FORMAT_ARG_TYPE_STRING;
|
||||
arguments[argumentCounter].value = &content.chars[startIndex];
|
||||
++argumentCounter;
|
||||
|
||||
uint32_t nullByteIndex = ffStrbufNextIndexC(&content, startIndex, '\0');
|
||||
|
||||
if(argumentCounter == numArgs)
|
||||
{
|
||||
uint8_t moduleIndex = (moduleCounter == 1 && nullByteIndex == content.length) ? 0 : moduleCounter;
|
||||
ffPrintFormatString(instance, moduleName, moduleIndex, customKeyFormat, formatString, NULL, numArgs, arguments);
|
||||
++moduleCounter;
|
||||
argumentCounter = 0;
|
||||
}
|
||||
|
||||
startIndex = nullByteIndex + 1;
|
||||
}
|
||||
|
||||
free(arguments);
|
||||
ffStrbufDestroy(&content);
|
||||
|
||||
return moduleCounter > 1;
|
||||
}
|
||||
|
||||
bool ffPrintFromCache(FFinstance* instance, const char* moduleName, const FFstrbuf* customKeyFormat, const FFstrbuf* formatString, uint32_t numArgs)
|
||||
{
|
||||
if(instance->config.recache)
|
||||
return false;
|
||||
|
||||
if(formatString == NULL || formatString->length == 0)
|
||||
return printCachedValue(instance, moduleName, customKeyFormat);
|
||||
else
|
||||
return printCachedFormat(instance, moduleName, customKeyFormat, formatString, numArgs);
|
||||
}
|
||||
|
||||
void ffPrintAndAppendToCache(FFinstance* instance, const char* moduleName, uint8_t moduleIndex, const FFstrbuf* customKeyFormat, FFcache* cache, const FFstrbuf* value, const FFstrbuf* formatString, uint32_t numArgs, const FFformatarg* arguments)
|
||||
{
|
||||
if(formatString == NULL || formatString->length == 0)
|
||||
{
|
||||
ffPrintLogoAndKey(instance, moduleName, moduleIndex, customKeyFormat);
|
||||
ffStrbufPutTo(value, stdout);
|
||||
}
|
||||
else
|
||||
{
|
||||
ffPrintFormatString(instance, moduleName, moduleIndex, customKeyFormat, formatString, NULL, numArgs, arguments);
|
||||
}
|
||||
|
||||
if(cache->value != NULL)
|
||||
{
|
||||
ffStrbufWriteTo(value, cache->value);
|
||||
fputc('\0', cache->value);
|
||||
}
|
||||
|
||||
if(cache->split == NULL)
|
||||
return;
|
||||
|
||||
for(uint32_t i = 0; i < numArgs; i++)
|
||||
{
|
||||
FFstrbuf buffer;
|
||||
ffStrbufInitA(&buffer, 64);
|
||||
ffFormatAppendFormatArg(&buffer, &arguments[i]);
|
||||
ffStrbufWriteTo(&buffer, cache->split);
|
||||
ffStrbufDestroy(&buffer);
|
||||
fputc('\0', cache->split);
|
||||
}
|
||||
}
|
||||
|
||||
void ffPrintAndSaveToCache(FFinstance* instance, const char* moduleName, const FFstrbuf* customKeyFormat, const FFstrbuf* value, const FFstrbuf* formatString, uint32_t numArgs, const FFformatarg* arguments)
|
||||
{
|
||||
FFcache cache;
|
||||
ffCacheOpenWrite(instance, moduleName, &cache);
|
||||
ffPrintAndAppendToCache(instance, moduleName, 0, customKeyFormat, &cache, value, formatString, numArgs, arguments);
|
||||
ffCacheClose(&cache);
|
||||
}
|
||||
|
||||
void ffCacheValidate(FFinstance* instance)
|
||||
{
|
||||
FFstrbuf path;
|
||||
ffStrbufInitA(&path, 64);
|
||||
ffGetCacheFilePath(instance, "cacheversion", "ffv", &path);
|
||||
|
||||
FFstrbuf content;
|
||||
ffStrbufInit(&content);
|
||||
ffAppendFileContent(path.chars, &content);
|
||||
|
||||
bool isSameVersion = ffStrbufCompS(&content, FASTFETCH_PROJECT_VERSION) == 0;
|
||||
ffStrbufDestroy(&content);
|
||||
if(isSameVersion)
|
||||
{
|
||||
ffStrbufDestroy(&path);
|
||||
return;
|
||||
}
|
||||
|
||||
instance->config.recache = true;
|
||||
|
||||
FFstrbuf version;
|
||||
ffStrbufInit(&version);
|
||||
ffStrbufAppendS(&version, FASTFETCH_PROJECT_VERSION);
|
||||
ffWriteFileContent(path.chars, &version);
|
||||
ffStrbufDestroy(&version);
|
||||
|
||||
ffStrbufDestroy(&path);
|
||||
}
|
||||
|
||||
void ffCacheOpenWrite(FFinstance* instance, const char* moduleName, FFcache* cache)
|
||||
{
|
||||
FFstrbuf cacheFileValue;
|
||||
ffStrbufInitA(&cacheFileValue, 64);
|
||||
ffGetCacheFilePath(instance, moduleName, FF_IO_CACHE_VALUE_EXTENSION, &cacheFileValue);
|
||||
cache->value = fopen(cacheFileValue.chars, "w");
|
||||
ffStrbufDestroy(&cacheFileValue);
|
||||
|
||||
FFstrbuf cacheFileSplit;
|
||||
ffStrbufInitA(&cacheFileSplit, 64);
|
||||
ffGetCacheFilePath(instance, moduleName, FF_IO_CACHE_SPLIT_EXTENSION, &cacheFileSplit);
|
||||
cache->split = fopen(cacheFileSplit.chars, "w");
|
||||
ffStrbufDestroy(&cacheFileSplit);
|
||||
}
|
||||
|
||||
void ffCacheClose(FFcache* cache)
|
||||
{
|
||||
if(cache->value != NULL)
|
||||
fclose(cache->value);
|
||||
|
||||
if(cache->split != NULL)
|
||||
fclose(cache->split);
|
||||
}
|
||||
|
||||
bool ffParsePropFileValues(const char* filename, uint32_t numQueries, FFpropquery* queries)
|
||||
{
|
||||
bool* searchedValues = malloc(sizeof(bool) * numQueries);
|
||||
bool allSet = true;
|
||||
for(uint32_t i = 0; i < numQueries; i++)
|
||||
{
|
||||
if((searchedValues[i] = queries[i].buffer->length == 0))
|
||||
allSet = false;
|
||||
}
|
||||
|
||||
if(allSet)
|
||||
{
|
||||
free(searchedValues);
|
||||
return true;
|
||||
}
|
||||
|
||||
FILE* file = fopen(filename, "r");
|
||||
if(file == NULL)
|
||||
return false;
|
||||
|
||||
char* line = NULL;
|
||||
size_t len = 0;
|
||||
|
||||
while (getline(&line, &len, file) != -1)
|
||||
{
|
||||
for(uint32_t i = 0; i < numQueries; i++)
|
||||
{
|
||||
if(!searchedValues[i])
|
||||
continue;
|
||||
|
||||
uint32_t currentLength = queries[i].buffer->length;
|
||||
queries[i].buffer->length = 0;
|
||||
if(!ffGetPropValue(line, queries[i].start, queries[i].buffer))
|
||||
queries[i].buffer->length = currentLength;
|
||||
}
|
||||
}
|
||||
|
||||
free(searchedValues);
|
||||
|
||||
if(line != NULL)
|
||||
free(line);
|
||||
|
||||
fclose(file);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool ffParsePropFile(const char* filename, const char* start, FFstrbuf* buffer)
|
||||
{
|
||||
return ffParsePropFileValues(filename, 1, (FFpropquery[]){{start, buffer}});
|
||||
}
|
||||
|
||||
bool ffParsePropFileHomeValues(const FFinstance* instance, const char* relativeFile, uint32_t numQueries, FFpropquery* queries)
|
||||
{
|
||||
FFstrbuf absolutePath;
|
||||
ffStrbufInitA(&absolutePath, 64);
|
||||
ffStrbufAppendS(&absolutePath, instance->state.passwd->pw_dir);
|
||||
ffStrbufAppendC(&absolutePath, '/');
|
||||
ffStrbufAppendS(&absolutePath, relativeFile);
|
||||
|
||||
bool result = ffParsePropFileValues(absolutePath.chars, numQueries, queries);
|
||||
|
||||
ffStrbufDestroy(&absolutePath);
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
bool ffParsePropFileHome(const FFinstance* instance, const char* relativeFile, const char* start, FFstrbuf* buffer)
|
||||
{
|
||||
return ffParsePropFileHomeValues(instance, relativeFile, 1, (FFpropquery[]){{start, buffer}});
|
||||
}
|
||||
|
||||
bool ffParsePropFileConfigValues(const FFinstance* instance, const char* relativeFile, uint32_t numQueries, FFpropquery* queries)
|
||||
{
|
||||
bool foundAFile = false;
|
||||
|
||||
for(uint32_t i = 0; i < instance->state.configDirs.length; i++)
|
||||
{
|
||||
FFstrbuf* baseDir = (FFstrbuf*) ffListGet(&instance->state.configDirs, i);
|
||||
uint32_t baseDirLength = baseDir->length;
|
||||
|
||||
if(*relativeFile != '/')
|
||||
ffStrbufAppendC(baseDir, '/');
|
||||
|
||||
ffStrbufAppendS(baseDir, relativeFile);
|
||||
|
||||
if(ffParsePropFileValues(baseDir->chars, numQueries, queries))
|
||||
foundAFile = true;
|
||||
|
||||
ffStrbufSubstrBefore(baseDir, baseDirLength);
|
||||
|
||||
bool allSet = true;
|
||||
for(uint32_t k = 0; k < numQueries; k++)
|
||||
{
|
||||
if(queries[k].buffer->length == 0)
|
||||
{
|
||||
allSet = false;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if(allSet)
|
||||
break;
|
||||
}
|
||||
|
||||
return foundAFile;
|
||||
}
|
||||
|
||||
bool ffParsePropFileConfig(const FFinstance* instance, const char* relativeFile, const char* start, FFstrbuf* buffer)
|
||||
{
|
||||
return ffParsePropFileConfigValues(instance, relativeFile, 1, (FFpropquery[]){{start, buffer}});
|
||||
}
|
||||
#include <stdarg.h>
|
||||
#include <termios.h>
|
||||
#include <poll.h>
|
||||
|
||||
bool ffWriteFDContent(int fd, const FFstrbuf* content)
|
||||
{
|
||||
return write(fd, content->chars, content->length) != -1;
|
||||
}
|
||||
|
||||
void ffWriteFileContent(const char* fileName, const FFstrbuf* content)
|
||||
static void createSubfolders(const char* fileName)
|
||||
{
|
||||
int fd = open(fileName, O_WRONLY | O_CREAT | O_TRUNC, S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH);
|
||||
if(fd == -1)
|
||||
return;
|
||||
FFstrbuf path;
|
||||
ffStrbufInit(&path);
|
||||
|
||||
ffWriteFDContent(fd, content);
|
||||
while(*fileName != '\0')
|
||||
{
|
||||
ffStrbufAppendC(&path, *fileName);
|
||||
if(*fileName == '/')
|
||||
mkdir(path.chars, S_IRWXU | S_IRGRP | S_IROTH);
|
||||
++fileName;
|
||||
}
|
||||
|
||||
ffStrbufDestroy(&path);
|
||||
}
|
||||
|
||||
bool ffWriteFileContent(const char* fileName, const FFstrbuf* content)
|
||||
{
|
||||
int openFlagsModes = O_WRONLY | O_CREAT | O_TRUNC;
|
||||
int openFlagsRights = S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH;
|
||||
|
||||
int fd = open(fileName, openFlagsModes, openFlagsRights);
|
||||
if(fd == -1)
|
||||
{
|
||||
createSubfolders(fileName);
|
||||
fd = open(fileName, openFlagsModes, openFlagsRights);
|
||||
if(fd == -1)
|
||||
return false;
|
||||
}
|
||||
|
||||
bool ret = ffWriteFDContent(fd, content);
|
||||
|
||||
close(fd);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
void ffAppendFDContent(int fd, FFstrbuf* buffer)
|
||||
@@ -491,15 +122,50 @@ void ffSuppressIO(bool suppress)
|
||||
dup2(suppress ? nullFile : origErr, STDERR_FILENO);
|
||||
}
|
||||
|
||||
void ffPrintColor(const FFstrbuf* colorValue)
|
||||
{
|
||||
fputs("\033[", stdout);
|
||||
ffStrbufWriteTo(colorValue, stdout);
|
||||
fputc('m', stdout);
|
||||
}
|
||||
|
||||
bool ffFileExists(const char* fileName, mode_t mode)
|
||||
{
|
||||
struct stat fileStat;
|
||||
return stat(fileName, &fileStat) == 0 && ((fileStat.st_mode & S_IFMT) == mode);
|
||||
}
|
||||
|
||||
void ffGetTerminalResponse(const char* request, const char* format, ...)
|
||||
{
|
||||
struct termios oldTerm, newTerm;
|
||||
if(tcgetattr(STDIN_FILENO, &oldTerm) == -1)
|
||||
return;
|
||||
|
||||
newTerm = oldTerm;
|
||||
newTerm.c_lflag &= (tcflag_t) ~(ICANON | ECHO);
|
||||
if(tcsetattr(STDIN_FILENO, TCSANOW, &newTerm) == -1)
|
||||
return;
|
||||
|
||||
fputs(request, stdout);
|
||||
fflush(stdout);
|
||||
|
||||
struct pollfd pfd;
|
||||
pfd.fd = STDIN_FILENO;
|
||||
pfd.events = POLLIN;
|
||||
pfd.revents = 0;
|
||||
|
||||
//Give the terminal 35ms to respond
|
||||
if(poll(&pfd, 1, 35) <= 0)
|
||||
{
|
||||
tcsetattr(STDIN_FILENO, TCSANOW, &oldTerm);
|
||||
return;
|
||||
}
|
||||
|
||||
char buffer[512];
|
||||
ssize_t readed = read(STDIN_FILENO, buffer, sizeof(buffer) - 1);
|
||||
|
||||
tcsetattr(STDIN_FILENO, TCSANOW, &oldTerm);
|
||||
|
||||
if(readed <= 0)
|
||||
return;
|
||||
|
||||
buffer[readed] = '\0';
|
||||
|
||||
va_list args;
|
||||
va_start(args, format);
|
||||
vsscanf(buffer, format, args);
|
||||
va_end(args);
|
||||
}
|
||||
|
||||
+34
-4
@@ -2,6 +2,33 @@
|
||||
|
||||
#include <stdarg.h>
|
||||
|
||||
static void* libraryLoad(const char* path, int maxVersion)
|
||||
{
|
||||
void* result = dlopen(path, RTLD_LAZY);
|
||||
if(result != NULL)
|
||||
return result;
|
||||
|
||||
FFstrbuf pathbuf;
|
||||
ffStrbufInitA(&pathbuf, 64);
|
||||
ffStrbufAppendS(&pathbuf, path);
|
||||
ffStrbufAppendC(&pathbuf, '.');
|
||||
|
||||
for(int i = maxVersion; i >= 0; --i)
|
||||
{
|
||||
uint32_t originalLength = pathbuf.length;
|
||||
ffStrbufAppendF(&pathbuf, "%i", i);
|
||||
|
||||
result = dlopen(pathbuf.chars, RTLD_LAZY);
|
||||
if(result != NULL)
|
||||
break;
|
||||
|
||||
ffStrbufSubstrBefore(&pathbuf, originalLength);
|
||||
}
|
||||
|
||||
ffStrbufDestroy(&pathbuf);
|
||||
return result;
|
||||
}
|
||||
|
||||
void* ffLibraryLoad(const FFstrbuf* userProvidedName, ...)
|
||||
{
|
||||
if(userProvidedName->length > 0)
|
||||
@@ -11,12 +38,15 @@ void* ffLibraryLoad(const FFstrbuf* userProvidedName, ...)
|
||||
va_start(defaultNames, userProvidedName);
|
||||
|
||||
void* result = NULL;
|
||||
const char* name = va_arg(defaultNames, const char*);
|
||||
|
||||
while(result == NULL && name != NULL)
|
||||
while(result == NULL)
|
||||
{
|
||||
result = dlopen(name, RTLD_LAZY);
|
||||
name = va_arg(defaultNames, const char*);
|
||||
const char* path = va_arg(defaultNames, const char*);
|
||||
if(path == NULL)
|
||||
break;
|
||||
|
||||
int maxVersion = va_arg(defaultNames, int);
|
||||
result = libraryLoad(path, maxVersion);
|
||||
}
|
||||
|
||||
va_end(defaultNames);
|
||||
|
||||
-1399
File diff suppressed because it is too large
Load Diff
+39
-328
@@ -1,8 +1,45 @@
|
||||
#include "fastfetch.h"
|
||||
|
||||
#include <string.h>
|
||||
bool ffStrSet(const char* str)
|
||||
{
|
||||
if(str == NULL)
|
||||
return false;
|
||||
|
||||
void ffGetGtkPretty(FFstrbuf* buffer, const FFstrbuf* gtk2, const FFstrbuf* gtk3, const FFstrbuf* gtk4)
|
||||
while(*str != '\0')
|
||||
{
|
||||
if(*str != ' ' && *str != '\t' && *str != '\n' && *str != '\r')
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
void ffParseSemver(FFstrbuf* buffer, const FFstrbuf* major, const FFstrbuf* minor, const FFstrbuf* patch)
|
||||
{
|
||||
if(major->length > 0)
|
||||
ffStrbufAppend(buffer, major);
|
||||
else if(minor->length > 0 || patch->length > 0)
|
||||
ffStrbufAppendC(buffer, '1');
|
||||
|
||||
if(minor->length == 0 && patch->length == 0)
|
||||
return;
|
||||
|
||||
ffStrbufAppendC(buffer, '.');
|
||||
|
||||
if(minor->length > 0)
|
||||
ffStrbufAppend(buffer, minor);
|
||||
else if(patch->length > 0)
|
||||
ffStrbufAppendC(buffer, '0');
|
||||
|
||||
if(patch->length == 0)
|
||||
return;
|
||||
|
||||
ffStrbufAppendC(buffer, '.');
|
||||
|
||||
ffStrbufAppend(buffer, patch);
|
||||
}
|
||||
|
||||
void ffParseGTK(FFstrbuf* buffer, const FFstrbuf* gtk2, const FFstrbuf* gtk3, const FFstrbuf* gtk4)
|
||||
{
|
||||
if(gtk2->length > 0 && gtk3->length > 0 && gtk4->length > 0)
|
||||
{
|
||||
@@ -81,329 +118,3 @@ void ffGetGtkPretty(FFstrbuf* buffer, const FFstrbuf* gtk2, const FFstrbuf* gtk3
|
||||
ffStrbufAppendS(buffer, " [GTK4]");
|
||||
}
|
||||
}
|
||||
|
||||
static void fontInit(FFfont* font)
|
||||
{
|
||||
ffStrbufInit(&font->pretty);
|
||||
ffStrbufInit(&font->name);
|
||||
ffStrbufInitA(&font->size, 4);
|
||||
ffListInitA(&font->styles, sizeof(FFstrbuf), 4);
|
||||
}
|
||||
|
||||
static void fontInitPretty(FFfont* font)
|
||||
{
|
||||
ffStrbufAppend(&font->pretty, &font->name);
|
||||
|
||||
if(font->size.length == 0 && font->styles.length == 0)
|
||||
return;
|
||||
else if(font->pretty.length == 0)
|
||||
ffStrbufAppendS(&font->pretty, "default");
|
||||
|
||||
ffStrbufAppendS(&font->pretty, " (");
|
||||
|
||||
if(font->size.length > 0)
|
||||
{
|
||||
ffStrbufAppend(&font->pretty, &font->size);
|
||||
ffStrbufAppendS(&font->pretty, "pt");
|
||||
|
||||
if(font->styles.length > 0)
|
||||
ffStrbufAppendS(&font->pretty, ", ");
|
||||
}
|
||||
|
||||
for(uint32_t i = 0; i < font->styles.length; i++)
|
||||
{
|
||||
ffStrbufAppend(&font->pretty, ffListGet(&font->styles, i));
|
||||
|
||||
if(i < font->styles.length - 1)
|
||||
ffStrbufAppendS(&font->pretty, ", ");
|
||||
}
|
||||
|
||||
ffStrbufAppendC(&font->pretty, ')');
|
||||
}
|
||||
|
||||
void ffFontInitQt(FFfont* font, const char* data)
|
||||
{
|
||||
fontInit(font);
|
||||
|
||||
//See https://doc.qt.io/qt-5/qfont.html#toString
|
||||
|
||||
//Family
|
||||
while(*data != ',' && *data != '\0')
|
||||
{
|
||||
ffStrbufAppendC(&font->name, *data);
|
||||
++data;
|
||||
}
|
||||
if(*data != '\0')
|
||||
++data;
|
||||
ffStrbufTrim(&font->name, ' ');
|
||||
|
||||
//Size
|
||||
while(*data != ',' && *data != '\0')
|
||||
{
|
||||
ffStrbufAppendC(&font->size, *data);
|
||||
++data;
|
||||
}
|
||||
if(*data != '\0')
|
||||
++data;
|
||||
ffStrbufTrim(&font->size, ' ');
|
||||
|
||||
#define FF_FONT_QT_SKIP_VALUE \
|
||||
while(*data != ',' && *data != '\0') \
|
||||
++data; \
|
||||
if(*data != '\0') \
|
||||
++data;
|
||||
|
||||
FF_FONT_QT_SKIP_VALUE //Pixel size
|
||||
FF_FONT_QT_SKIP_VALUE //Style hint
|
||||
FF_FONT_QT_SKIP_VALUE //Font weight
|
||||
FF_FONT_QT_SKIP_VALUE //Font style
|
||||
FF_FONT_QT_SKIP_VALUE //Underline
|
||||
FF_FONT_QT_SKIP_VALUE //Strike out
|
||||
FF_FONT_QT_SKIP_VALUE //Fixed pitch
|
||||
FF_FONT_QT_SKIP_VALUE //Always 0
|
||||
|
||||
#undef FF_FONT_QT_SKIP_VALUE
|
||||
|
||||
while(*data != '\0')
|
||||
{
|
||||
while(*data == ' ')
|
||||
++data;
|
||||
|
||||
if(*data == '\0')
|
||||
break;
|
||||
|
||||
FFstrbuf* style = ffListAdd(&font->styles);
|
||||
ffStrbufInit(style);
|
||||
while(*data != ' ' && *data != '\0')
|
||||
{
|
||||
ffStrbufAppendC(style, *data);
|
||||
++data;
|
||||
}
|
||||
}
|
||||
|
||||
fontInitPretty(font);
|
||||
}
|
||||
|
||||
static void fontPangoParseWord(const char** data, FFfont* font, FFstrbuf* alternativeBuffer)
|
||||
{
|
||||
while(**data == ' ' || **data == '\t' || **data == ',')
|
||||
++(*data);
|
||||
|
||||
const char* wordStart = *data;
|
||||
|
||||
while(**data != ' ' && **data != '\t' && **data != ',' && **data != '\0' && **data != '`' && **data != '\\')
|
||||
++(*data);
|
||||
|
||||
uint32_t wordLength = (uint32_t) (*data - wordStart);
|
||||
if(wordLength == 0)
|
||||
return;
|
||||
|
||||
if(**data == '\0' || **data == '`' || **data == '\\')
|
||||
{
|
||||
ffStrbufAppendNS(&font->size, wordLength, wordStart);
|
||||
if(ffStrbufEndsWithS(&font->size, "px"))
|
||||
ffStrbufSubstrBefore(&font->size, font->size.length - 2);
|
||||
|
||||
double dummy;
|
||||
if(sscanf(font->size.chars, "%lf", &dummy) == 1)
|
||||
return;
|
||||
|
||||
ffStrbufClear(&font->size);
|
||||
}
|
||||
|
||||
if(
|
||||
strncasecmp(wordStart, "Ultra", 5) == 0 ||
|
||||
strncasecmp(wordStart, "Extra", 5) == 0 ||
|
||||
strncasecmp(wordStart, "Semi", 4) == 0 ||
|
||||
strncasecmp(wordStart, "Demi", 4) == 0 ||
|
||||
strncasecmp(wordStart, "Normal", wordLength) == 0 ||
|
||||
strncasecmp(wordStart, "Roman", wordLength) == 0 ||
|
||||
strncasecmp(wordStart, "Oblique", wordLength) == 0 ||
|
||||
strncasecmp(wordStart, "Italic", wordLength) == 0 ||
|
||||
strncasecmp(wordStart, "Thin", wordLength) == 0 ||
|
||||
strncasecmp(wordStart, "Light", wordLength) == 0 ||
|
||||
strncasecmp(wordStart, "Bold", wordLength) == 0 ||
|
||||
strncasecmp(wordStart, "Black", wordLength) == 0 ||
|
||||
strncasecmp(wordStart, "Condensed", wordLength) == 0 ||
|
||||
strncasecmp(wordStart, "Expanded", wordLength) == 0
|
||||
) {
|
||||
if(alternativeBuffer == NULL)
|
||||
{
|
||||
alternativeBuffer = ffListAdd(&font->styles);
|
||||
ffStrbufInit(alternativeBuffer);
|
||||
}
|
||||
|
||||
ffStrbufAppendNSExludingC(alternativeBuffer, wordLength, wordStart, '-');
|
||||
|
||||
if(
|
||||
strncasecmp(wordStart, "Ultra ", 6) == 0 ||
|
||||
strncasecmp(wordStart, "Extra ", 6) == 0 ||
|
||||
strncasecmp(wordStart, "Semi ", 5) == 0 ||
|
||||
strncasecmp(wordStart, "Demi ", 5) == 0
|
||||
) {
|
||||
fontPangoParseWord(data, font, alternativeBuffer);
|
||||
}
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
if(alternativeBuffer != NULL)
|
||||
{
|
||||
ffStrbufAppendNSExludingC(alternativeBuffer, wordLength, wordStart, '-');
|
||||
return;
|
||||
}
|
||||
|
||||
if(font->name.length > 0)
|
||||
ffStrbufAppendC(&font->name, ' ');
|
||||
ffStrbufAppendNS(&font->name, wordLength, wordStart);
|
||||
}
|
||||
|
||||
void ffFontInitPango(FFfont* font, const char* data)
|
||||
{
|
||||
fontInit(font);
|
||||
|
||||
while(*data != '\0' && *data != '`' && *data != '\\')
|
||||
fontPangoParseWord(&data, font, NULL);
|
||||
|
||||
fontInitPretty(font);
|
||||
}
|
||||
|
||||
void ffFontInitCopy(FFfont* font, const char* name)
|
||||
{
|
||||
fontInit(font);
|
||||
ffStrbufAppendS(&font->name, name);
|
||||
fontInitPretty(font);
|
||||
}
|
||||
|
||||
void ffFontDestroy(FFfont* font)
|
||||
{
|
||||
ffStrbufDestroy(&font->pretty);
|
||||
ffStrbufDestroy(&font->name);
|
||||
ffStrbufDestroy(&font->size);
|
||||
|
||||
for(uint32_t i = 0; i < font->styles.length; i++)
|
||||
ffStrbufDestroy(ffListGet(&font->styles, i));
|
||||
ffListDestroy(&font->styles);
|
||||
}
|
||||
|
||||
static bool getPropValueLine(const char** line, const char* start, FFstrbuf* buffer)
|
||||
{
|
||||
if(**line == '\0')
|
||||
return false;
|
||||
|
||||
//Skip any amount of whitespace at the begin of line
|
||||
while(**line == ' ' || **line == '\t')
|
||||
++(*line);
|
||||
|
||||
while(*start != '\0')
|
||||
{
|
||||
// Any amount of whitespace in the format string matches any amount of whitespace in the line, even none
|
||||
if(*start == ' ' || *start == '\t')
|
||||
{
|
||||
while(*start == ' ' || *start == '\t')
|
||||
++start;
|
||||
|
||||
while(**line == ' ' || **line == '\t')
|
||||
++(*line);
|
||||
|
||||
continue;
|
||||
}
|
||||
|
||||
//Line doesn't match start, skip it
|
||||
if(**line != *start || **line == '\0')
|
||||
return false;
|
||||
|
||||
//Line and start match, continue testing
|
||||
++(*line);
|
||||
++start;
|
||||
}
|
||||
|
||||
char valueEnd = '\n';
|
||||
|
||||
//Allow faster parsing of XML
|
||||
if(*(*line - 1) == '>')
|
||||
valueEnd = '<';
|
||||
|
||||
//Skip any amount of whitespace at the begin of the value
|
||||
while(**line == ' ' || **line == '\t')
|
||||
++(*line);
|
||||
|
||||
//Allow faster parsing of quotet values
|
||||
if(**line == '"' || **line == '\'')
|
||||
{
|
||||
valueEnd = **line;
|
||||
++(*line);
|
||||
}
|
||||
|
||||
//Copy the value to the buffer
|
||||
while(**line != valueEnd && **line != '\n' && **line != '\0')
|
||||
{
|
||||
ffStrbufAppendC(buffer, **line);
|
||||
++(*line);
|
||||
}
|
||||
|
||||
ffStrbufTrimRight(buffer, ' ');
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool ffGetPropValue(const char* line, const char* start, FFstrbuf* buffer)
|
||||
{
|
||||
return getPropValueLine(&line, start, buffer);
|
||||
}
|
||||
|
||||
bool ffGetPropValueFromLines(const char* lines, const char* start, FFstrbuf* buffer)
|
||||
{
|
||||
while(!getPropValueLine(&lines, start, buffer))
|
||||
{
|
||||
while(*lines != '\0' && *lines != '\n')
|
||||
++lines;
|
||||
|
||||
if(*lines == '\0')
|
||||
return false;
|
||||
|
||||
//Skip '\n'
|
||||
++lines;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
void ffParseSemver(FFstrbuf* buffer, const FFstrbuf* major, const FFstrbuf* minor, const FFstrbuf* patch)
|
||||
{
|
||||
if(major->length > 0)
|
||||
ffStrbufAppend(buffer, major);
|
||||
else if(minor->length > 0 || patch->length > 0)
|
||||
ffStrbufAppendC(buffer, '1');
|
||||
|
||||
if(minor->length == 0 && patch->length == 0)
|
||||
return;
|
||||
|
||||
ffStrbufAppendC(buffer, '.');
|
||||
|
||||
if(minor->length > 0)
|
||||
ffStrbufAppend(buffer, minor);
|
||||
else if(patch->length > 0)
|
||||
ffStrbufAppendC(buffer, '0');
|
||||
|
||||
if(patch->length == 0)
|
||||
return;
|
||||
|
||||
ffStrbufAppendC(buffer, '.');
|
||||
|
||||
ffStrbufAppend(buffer, patch);
|
||||
}
|
||||
|
||||
bool ffStrSet(const char* str)
|
||||
{
|
||||
if(str == NULL)
|
||||
return false;
|
||||
|
||||
while(*str != '\0')
|
||||
{
|
||||
if(*str != ' ' && *str != '\t' && *str != '\n' && *str != '\r')
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
@@ -0,0 +1,62 @@
|
||||
#include "fastfetch.h"
|
||||
|
||||
void ffPrintError(FFinstance* instance, const char* moduleName, uint8_t moduleIndex, const FFstrbuf* customKeyFormat, const FFstrbuf* formatString, uint32_t numFormatArgs, const char* message, ...)
|
||||
{
|
||||
if(!instance->config.showErrors)
|
||||
return;
|
||||
|
||||
va_list arguments;
|
||||
va_start(arguments, message);
|
||||
|
||||
if(formatString == NULL || formatString->length == 0)
|
||||
{
|
||||
ffPrintLogoAndKey(instance, moduleName, moduleIndex, customKeyFormat);
|
||||
fputs(FASTFETCH_TEXT_MODIFIER_ERROR, stdout);
|
||||
vprintf(message, arguments);
|
||||
puts(FASTFETCH_TEXT_MODIFIER_RESET);
|
||||
}
|
||||
else
|
||||
{
|
||||
FF_STRBUF_CREATE(error);
|
||||
ffStrbufAppendVF(&error, message, arguments);
|
||||
|
||||
// calloc sets all to 0 and FF_FORMAT_ARG_TYPE_NULL also has value 0 so we don't need to explictly set it
|
||||
FFformatarg* nullArgs = calloc(numFormatArgs, sizeof(FFformatarg));
|
||||
|
||||
ffPrintFormatString(instance, moduleName, moduleIndex, customKeyFormat, formatString, &error, numFormatArgs, nullArgs);
|
||||
|
||||
free(nullArgs);
|
||||
ffStrbufDestroy(&error);
|
||||
}
|
||||
|
||||
va_end(arguments);
|
||||
}
|
||||
|
||||
void ffPrintFormatString(FFinstance* instance, const char* moduleName, uint8_t moduleIndex, const FFstrbuf* customKeyFormat, const FFstrbuf* formatString, const FFstrbuf* error, uint32_t numArgs, const FFformatarg* arguments)
|
||||
{
|
||||
FFstrbuf buffer;
|
||||
ffStrbufInitA(&buffer, 256);
|
||||
|
||||
ffParseFormatString(&buffer, formatString, error, numArgs, arguments);
|
||||
|
||||
if(buffer.length > 0)
|
||||
{
|
||||
ffPrintLogoAndKey(instance, moduleName, moduleIndex, customKeyFormat);
|
||||
ffStrbufPutTo(&buffer, stdout);
|
||||
}
|
||||
|
||||
ffStrbufDestroy(&buffer);
|
||||
}
|
||||
|
||||
void ffPrintColor(const FFstrbuf* colorValue)
|
||||
{
|
||||
fputs("\033[", stdout);
|
||||
ffStrbufWriteTo(colorValue, stdout);
|
||||
fputc('m', stdout);
|
||||
}
|
||||
|
||||
void ffPrintCharTimes(char c, uint32_t times)
|
||||
{
|
||||
for(uint32_t i = 0; i < times; i++)
|
||||
putchar(c);
|
||||
}
|
||||
@@ -0,0 +1,202 @@
|
||||
#include "fastfetch.h"
|
||||
|
||||
static bool parsePropLinePointer(const char** line, const char* start, FFstrbuf* buffer)
|
||||
{
|
||||
if(**line == '\0')
|
||||
return false;
|
||||
|
||||
//Skip any amount of whitespace at the begin of line
|
||||
while(**line == ' ' || **line == '\t')
|
||||
++(*line);
|
||||
|
||||
while(*start != '\0')
|
||||
{
|
||||
// Any amount of whitespace in the format string matches any amount of whitespace in the line, even none
|
||||
if(*start == ' ' || *start == '\t')
|
||||
{
|
||||
while(*start == ' ' || *start == '\t')
|
||||
++start;
|
||||
|
||||
while(**line == ' ' || **line == '\t')
|
||||
++(*line);
|
||||
|
||||
continue;
|
||||
}
|
||||
|
||||
//Line doesn't match start, skip it
|
||||
if(**line != *start || **line == '\0')
|
||||
return false;
|
||||
|
||||
//Line and start match, continue testing
|
||||
++(*line);
|
||||
++start;
|
||||
}
|
||||
|
||||
char valueEnd = '\n';
|
||||
|
||||
//Allow faster parsing of XML
|
||||
if(*(*line - 1) == '>')
|
||||
valueEnd = '<';
|
||||
|
||||
//Skip any amount of whitespace at the begin of the value
|
||||
while(**line == ' ' || **line == '\t')
|
||||
++(*line);
|
||||
|
||||
//Allow faster parsing of quotet values
|
||||
if(**line == '"' || **line == '\'')
|
||||
{
|
||||
valueEnd = **line;
|
||||
++(*line);
|
||||
}
|
||||
|
||||
//Copy the value to the buffer
|
||||
while(**line != valueEnd && **line != '\n' && **line != '\0')
|
||||
{
|
||||
ffStrbufAppendC(buffer, **line);
|
||||
++(*line);
|
||||
}
|
||||
|
||||
ffStrbufTrimRight(buffer, ' ');
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool ffParsePropLine(const char* line, const char* start, FFstrbuf* buffer)
|
||||
{
|
||||
return parsePropLinePointer(&line, start, buffer);
|
||||
}
|
||||
|
||||
bool ffParsePropLines(const char* lines, const char* start, FFstrbuf* buffer)
|
||||
{
|
||||
while(!parsePropLinePointer(&lines, start, buffer))
|
||||
{
|
||||
while(*lines != '\0' && *lines != '\n')
|
||||
++lines;
|
||||
|
||||
if(*lines == '\0')
|
||||
return false;
|
||||
|
||||
//Skip '\n'
|
||||
++lines;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
// The following functions return true if the file was found, independently if start was found
|
||||
// Buffers which already contain content are not overwritten
|
||||
// The last occurence of start in the first file will be the one used
|
||||
// The *Values methods always return true, if all properties were already found before, without testing if the file exists
|
||||
|
||||
bool ffParsePropFileValues(const char* filename, uint32_t numQueries, FFpropquery* queries)
|
||||
{
|
||||
bool* searchedValues = malloc(sizeof(bool) * numQueries);
|
||||
bool allSet = true;
|
||||
for(uint32_t i = 0; i < numQueries; i++)
|
||||
{
|
||||
if((searchedValues[i] = queries[i].buffer->length == 0))
|
||||
allSet = false;
|
||||
}
|
||||
|
||||
if(allSet)
|
||||
{
|
||||
free(searchedValues);
|
||||
return true;
|
||||
}
|
||||
|
||||
FILE* file = fopen(filename, "r");
|
||||
if(file == NULL)
|
||||
return false;
|
||||
|
||||
char* line = NULL;
|
||||
size_t len = 0;
|
||||
|
||||
while (getline(&line, &len, file) != -1)
|
||||
{
|
||||
for(uint32_t i = 0; i < numQueries; i++)
|
||||
{
|
||||
if(!searchedValues[i])
|
||||
continue;
|
||||
|
||||
uint32_t currentLength = queries[i].buffer->length;
|
||||
queries[i].buffer->length = 0;
|
||||
if(!ffParsePropLine(line, queries[i].start, queries[i].buffer))
|
||||
queries[i].buffer->length = currentLength;
|
||||
}
|
||||
}
|
||||
|
||||
free(searchedValues);
|
||||
|
||||
if(line != NULL)
|
||||
free(line);
|
||||
|
||||
fclose(file);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool ffParsePropFile(const char* filename, const char* start, FFstrbuf* buffer)
|
||||
{
|
||||
return ffParsePropFileValues(filename, 1, (FFpropquery[]){{start, buffer}});
|
||||
}
|
||||
|
||||
bool ffParsePropFileHomeValues(const FFinstance* instance, const char* relativeFile, uint32_t numQueries, FFpropquery* queries)
|
||||
{
|
||||
FFstrbuf absolutePath;
|
||||
ffStrbufInitA(&absolutePath, 64);
|
||||
ffStrbufAppendS(&absolutePath, instance->state.passwd->pw_dir);
|
||||
ffStrbufAppendC(&absolutePath, '/');
|
||||
ffStrbufAppendS(&absolutePath, relativeFile);
|
||||
|
||||
bool result = ffParsePropFileValues(absolutePath.chars, numQueries, queries);
|
||||
|
||||
ffStrbufDestroy(&absolutePath);
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
bool ffParsePropFileHome(const FFinstance* instance, const char* relativeFile, const char* start, FFstrbuf* buffer)
|
||||
{
|
||||
return ffParsePropFileHomeValues(instance, relativeFile, 1, (FFpropquery[]){{start, buffer}});
|
||||
}
|
||||
|
||||
bool ffParsePropFileConfigValues(const FFinstance* instance, const char* relativeFile, uint32_t numQueries, FFpropquery* queries)
|
||||
{
|
||||
bool foundAFile = false;
|
||||
|
||||
for(uint32_t i = 0; i < instance->state.configDirs.length; i++)
|
||||
{
|
||||
FFstrbuf* baseDir = (FFstrbuf*) ffListGet(&instance->state.configDirs, i);
|
||||
uint32_t baseDirLength = baseDir->length;
|
||||
|
||||
if(*relativeFile != '/')
|
||||
ffStrbufAppendC(baseDir, '/');
|
||||
|
||||
ffStrbufAppendS(baseDir, relativeFile);
|
||||
|
||||
if(ffParsePropFileValues(baseDir->chars, numQueries, queries))
|
||||
foundAFile = true;
|
||||
|
||||
ffStrbufSubstrBefore(baseDir, baseDirLength);
|
||||
|
||||
bool allSet = true;
|
||||
for(uint32_t k = 0; k < numQueries; k++)
|
||||
{
|
||||
if(queries[k].buffer->length == 0)
|
||||
{
|
||||
allSet = false;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if(allSet)
|
||||
break;
|
||||
}
|
||||
|
||||
return foundAFile;
|
||||
}
|
||||
|
||||
bool ffParsePropFileConfig(const FFinstance* instance, const char* relativeFile, const char* start, FFstrbuf* buffer)
|
||||
{
|
||||
return ffParsePropFileConfigValues(instance, relativeFile, 1, (FFpropquery[]){{start, buffer}});
|
||||
}
|
||||
+72
-3
@@ -1,6 +1,7 @@
|
||||
#include "fastfetch.h"
|
||||
|
||||
#include <pthread.h>
|
||||
#include <string.h>
|
||||
|
||||
#define FF_LIBRARY_DATA_LOAD_INIT(dataObject, userLibraryName, ...) \
|
||||
static dataObject data; \
|
||||
@@ -80,7 +81,7 @@ typedef struct GSettingsData
|
||||
|
||||
static const GSettingsData* getGSettingsData(FFinstance* instance)
|
||||
{
|
||||
FF_LIBRARY_DATA_LOAD_INIT(GSettingsData, instance->config.libGIO, "libgio-2.0.so", "libgio-2.0.so.0");
|
||||
FF_LIBRARY_DATA_LOAD_INIT(GSettingsData, instance->config.libGIO, "libgio-2.0.so", 1);
|
||||
|
||||
FF_LIBRARY_DATA_LOAD_SYMBOL(g_settings_schema_source_lookup)
|
||||
FF_LIBRARY_DATA_LOAD_SYMBOL(g_settings_schema_has_key)
|
||||
@@ -152,7 +153,7 @@ typedef struct DConfData
|
||||
|
||||
static const DConfData* getDConfData(FFinstance* instance)
|
||||
{
|
||||
FF_LIBRARY_DATA_LOAD_INIT(DConfData, instance->config.libDConf, "libdconf.so", "libdconf.so.1");
|
||||
FF_LIBRARY_DATA_LOAD_INIT(DConfData, instance->config.libDConf, "libdconf.so", 2);
|
||||
|
||||
FF_LIBRARY_DATA_LOAD_SYMBOL(dconf_client_read_full)
|
||||
FF_LIBRARY_DATA_LOAD_SYMBOL(dconf_client_new)
|
||||
@@ -222,7 +223,7 @@ typedef struct XFConfData
|
||||
|
||||
static const XFConfData* getXFConfData(FFinstance* instance)
|
||||
{
|
||||
FF_LIBRARY_DATA_LOAD_INIT(XFConfData, instance->config.libXFConf, "libxfconf-0.so", "libxfconf-0.so.3");
|
||||
FF_LIBRARY_DATA_LOAD_INIT(XFConfData, instance->config.libXFConf, "libxfconf-0.so", 4);
|
||||
|
||||
FF_LIBRARY_DATA_LOAD_SYMBOL(xfconf_channel_get)
|
||||
FF_LIBRARY_DATA_LOAD_SYMBOL(xfconf_channel_has_property)
|
||||
@@ -267,6 +268,74 @@ FFvariant ffSettingsGetXFConf(FFinstance* instance, const char* channelName, con
|
||||
}
|
||||
#endif //FF_HAVE_XFCONF
|
||||
|
||||
#ifdef FF_HAVE_SQLITE3
|
||||
#include <sqlite3.h>
|
||||
|
||||
typedef struct SQLiteData
|
||||
{
|
||||
FF_LIBRARY_SYMBOL(sqlite3_open_v2)
|
||||
FF_LIBRARY_SYMBOL(sqlite3_prepare_v2)
|
||||
FF_LIBRARY_SYMBOL(sqlite3_step)
|
||||
FF_LIBRARY_SYMBOL(sqlite3_data_count)
|
||||
FF_LIBRARY_SYMBOL(sqlite3_column_int)
|
||||
FF_LIBRARY_SYMBOL(sqlite3_finalize)
|
||||
FF_LIBRARY_SYMBOL(sqlite3_close)
|
||||
} SQLiteData;
|
||||
|
||||
static const SQLiteData* getSQLiteData(const FFinstance* instance)
|
||||
{
|
||||
FF_LIBRARY_DATA_LOAD_INIT(SQLiteData, instance->config.libSQLite3, "libsqlite3.so", 1);
|
||||
|
||||
FF_LIBRARY_DATA_LOAD_SYMBOL(sqlite3_open_v2)
|
||||
FF_LIBRARY_DATA_LOAD_SYMBOL(sqlite3_prepare_v2)
|
||||
FF_LIBRARY_DATA_LOAD_SYMBOL(sqlite3_step)
|
||||
FF_LIBRARY_DATA_LOAD_SYMBOL(sqlite3_data_count)
|
||||
FF_LIBRARY_DATA_LOAD_SYMBOL(sqlite3_column_int)
|
||||
FF_LIBRARY_DATA_LOAD_SYMBOL(sqlite3_finalize)
|
||||
FF_LIBRARY_DATA_LOAD_SYMBOL(sqlite3_close)
|
||||
|
||||
FF_LIBRARY_DATA_LOAD_RETURN
|
||||
}
|
||||
|
||||
int ffSettingsGetSQLite3Int(FFinstance* instance, const char* dbPath, const char* query)
|
||||
{
|
||||
const SQLiteData* data = getSQLiteData(instance);
|
||||
if(data == NULL)
|
||||
return 0;
|
||||
|
||||
sqlite3* db;
|
||||
if(data->ffsqlite3_open_v2(dbPath, &db, SQLITE_OPEN_READONLY, NULL) != SQLITE_OK)
|
||||
return 0;
|
||||
|
||||
sqlite3_stmt* stmt;
|
||||
if(data->ffsqlite3_prepare_v2(db, query, (int) strlen(query), &stmt, NULL) != SQLITE_OK)
|
||||
{
|
||||
data->ffsqlite3_close(db);
|
||||
return 0;
|
||||
}
|
||||
|
||||
if(data->ffsqlite3_step(stmt) != SQLITE_ROW || data->ffsqlite3_data_count(stmt) < 1)
|
||||
{
|
||||
data->ffsqlite3_finalize(stmt);
|
||||
data->ffsqlite3_close(db);
|
||||
return 0;
|
||||
}
|
||||
|
||||
int result = data->ffsqlite3_column_int(stmt, 0);
|
||||
|
||||
data->ffsqlite3_finalize(stmt);
|
||||
data->ffsqlite3_close(db);
|
||||
|
||||
return result;
|
||||
}
|
||||
#else //FF_HAVE_SQLITE3
|
||||
int ffSettingsGetSQLite3Int(FFinstance* instance, const char* dbPath, const char* query)
|
||||
{
|
||||
FF_UNUSED(instance, dbPath, query)
|
||||
return 0;
|
||||
}
|
||||
#endif //FF_HAVE_SQLITE3
|
||||
|
||||
#ifdef __ANDROID__
|
||||
#include <sys/system_properties.h>
|
||||
bool ffSettingsGetAndroidProperty(const char* propName, FFstrbuf* result) {
|
||||
|
||||
+79
-64
@@ -27,38 +27,17 @@
|
||||
# Get the default structure with "fastfetch --print-structure".
|
||||
#--structure $"FASTFETCH_DATATEXT_STRUCTURE$"
|
||||
|
||||
# Logo option:
|
||||
# Sets the logo to use.
|
||||
# List available logos with "fastfetch --list-logos".
|
||||
# Print available logos with "fastfetch --print-logos".
|
||||
# Must be the name of an available logo or a path to a text file containing a custom logo.
|
||||
# Default is the current distribution.
|
||||
#--logo arch
|
||||
# Multithreading option:
|
||||
# Sets if fastfetch should use multiple threads to detect the values.
|
||||
# Must be true or false.
|
||||
# Default is true.
|
||||
#--multithreading true
|
||||
|
||||
# Color option:
|
||||
# Sets the color of the keys.
|
||||
# Must be linux console color codes or the name of a color.
|
||||
# Default is the primary color of the logo.
|
||||
# Use "fastfetch --help color" to learn more and see examples.
|
||||
#--color magenta
|
||||
|
||||
# Spacing option:
|
||||
# Sets the amount of whitespace between the logo and keys.
|
||||
# Must be an positive integer.
|
||||
# Default is 4.
|
||||
#--spacing 4
|
||||
|
||||
# Separator option:
|
||||
# Sets the string placed between a key and its value.
|
||||
# Can be any string.
|
||||
# Default is ": ".
|
||||
#--separator ": "
|
||||
|
||||
# Offset option:
|
||||
# Sets the amount of whitespace before the logo. Can be negative to cut the logo.
|
||||
# Must be an integer.
|
||||
# Default is 0.
|
||||
#--offsetx 0
|
||||
# Slow operations option:
|
||||
# Sets if fastfetch is allowed to use known slow operations to detect more / better values.
|
||||
# Must be true or false.
|
||||
# Default is false.
|
||||
#--allow-slow-operations false
|
||||
|
||||
# Linewrap option:
|
||||
# Sets if fastfetch should disable linewrap during the run.
|
||||
@@ -72,35 +51,31 @@
|
||||
# Default is true.
|
||||
#--hide-cursor true
|
||||
|
||||
# Remaing logo option:
|
||||
# Sets if the remaining logo should be printed, if it is higher than the information printed.
|
||||
# Must be true or false.
|
||||
# Default is true.
|
||||
#--print-remaining-logo true
|
||||
# Logo option:
|
||||
# Sets the logo to use.
|
||||
# List available logos with "fastfetch --list-logos".
|
||||
# Print available logos with "fastfetch --print-logos".
|
||||
# Must be the name of an available logo or a path to a text file containing a custom logo.
|
||||
# Default is the current distribution.
|
||||
#--logo arch
|
||||
|
||||
# Multithreading option:
|
||||
# Sets if fastfetch should use multiple threads to detect the values.
|
||||
# Must be true or false.
|
||||
# Default is true.
|
||||
#--multithreading true
|
||||
# Logo type option:
|
||||
# Sets the logo type to use.
|
||||
# Must be auto, builtin, file, raw, sixel, kitty or chafa.
|
||||
# Default is auto.
|
||||
#--logo-type auto
|
||||
|
||||
# Slow operations option:
|
||||
# Sets if fastfetch is allowed to use known slow operations to detect more / better values.
|
||||
# Must be true or false.
|
||||
# Default is false.
|
||||
#--allow-slow-operations false
|
||||
# Logo width option:
|
||||
# Sets the width of the logo (in characters) if the logo is an image.
|
||||
# Must be a positive integer.
|
||||
# Default is 65.
|
||||
#--logo-width 65
|
||||
|
||||
# Color logo option:
|
||||
# Sets if the logo should be colored.
|
||||
# Must be true or false.
|
||||
# Default is true.
|
||||
#--color-logo true
|
||||
|
||||
# Separator string option:
|
||||
# Sets the string printed by the "separator" module (usually between title and rest of output)
|
||||
# Must be any string. It is repated / cut to fit perfectly.
|
||||
# Default is "-"
|
||||
#--separator-string -
|
||||
# Logo height option:
|
||||
# Sets the height of the logo (in characters) if the logo is an image.
|
||||
# Must be a positive integer.
|
||||
# Default is 0 (keeps aspect ration).
|
||||
#--logo-height 0
|
||||
|
||||
# Logo color options:
|
||||
# Overwrite a color in the logo. Also works for user provided logos.
|
||||
@@ -109,16 +84,53 @@
|
||||
# Default is the one specified by the logo.
|
||||
# Use "fastfetch --help color" to learn more and see examples.
|
||||
# Valid index range is [1-9].
|
||||
#--color-1 red
|
||||
#--color-2 32
|
||||
#--logo-color-1 red
|
||||
#--logo-color-2 32
|
||||
# [...]
|
||||
#--color-9 yellow
|
||||
#--logo-color-9 yellow
|
||||
|
||||
# User logo raw option:
|
||||
# Sets if color replacements should be performed on the user provided logo.
|
||||
# Logo padding option:
|
||||
# Adds a padding to the left and the right side of the logo.
|
||||
# Must be a positive integer.
|
||||
# Default is 0.
|
||||
#--logo-padding 0
|
||||
|
||||
# Logo padding left option:
|
||||
# Adds a padding to the left side of the logo.
|
||||
# Must be a positive integer.
|
||||
# Default is 0.
|
||||
#--logo-padding-left 0
|
||||
|
||||
# Logo padding right option:
|
||||
# Adds a padding to the right side of the logo.
|
||||
# Must be a positive integer.
|
||||
# Default is 0.
|
||||
#--logo-padding-right 0
|
||||
|
||||
# Logo print remaining option:
|
||||
# Sets if the remaining logo should be printed, it is has more lines than modules to show.
|
||||
# Must be true or false.
|
||||
# Default is true.
|
||||
#--color-raw true
|
||||
#--logo-print-remaining true
|
||||
|
||||
# Color option:
|
||||
# Sets the color of the keys.
|
||||
# Must be linux console color codes or the name of a color.
|
||||
# Default is the primary color of the logo.
|
||||
# Use "fastfetch --help color" to learn more and see examples.
|
||||
#--color magenta
|
||||
|
||||
# Separator option:
|
||||
# Sets the string placed between a key and its value.
|
||||
# Can be any string.
|
||||
# Default is ": ".
|
||||
#--separator ": "
|
||||
|
||||
# Separator string option:
|
||||
# Sets the string printed by the "separator" module (usually between title and rest of output)
|
||||
# Must be any string. It is repated / cut to fit perfectly.
|
||||
# Default is "-"
|
||||
#--separator-string -
|
||||
|
||||
# Public IP timeout option:
|
||||
# Sets the time to wait for the public ip server to respond.
|
||||
@@ -221,4 +233,7 @@
|
||||
#--lib-DConf /usr/lib/libdconf.so
|
||||
#--lib-DBus /usr/lib/libdbus-1.so
|
||||
#--lib-XFConf /usr/lib/libxfconf-0.so
|
||||
#--lib-rpm /usr/lib/librpm.so
|
||||
#--lib-sqlite3 /usr/lib/libsqlite3.so
|
||||
#--lib-imagemagick /usr/lib/libMagickCore-7.Q16HDRI.so
|
||||
#--lib-z /usr/lib/libz.so
|
||||
#--lib-chafa /usr/lib/libchafa.so
|
||||
|
||||
+42
-30
@@ -1,39 +1,48 @@
|
||||
Usage: fastfetch <options>
|
||||
|
||||
Informative options:
|
||||
-h, --help: shows this message and exits
|
||||
-h <command>, --help <command>: shows help for a specific command and exits
|
||||
-v --version: prints the version of fastfetch and exits
|
||||
--list-logos: list available logos and exits
|
||||
--list-modules: lists available modules and exits
|
||||
--list-presets: list presets fastfetch knows about and exits. They can be loaded with --load-config. (+)
|
||||
--list-features: list the supported features fastfetch was compiled with and exits. Mainly for development.
|
||||
--print-logos: show available logos and exits
|
||||
--print-config: prints the default config and exits
|
||||
--print-structure: prints the default stucture and exits
|
||||
-h,--help: shows this message and exits
|
||||
-h,--help <command>: shows help for a specific command and exits
|
||||
-v,--version: prints the version of fastfetch and exits
|
||||
--list-logos: list available logos and exits
|
||||
--list-modules: lists available modules and exits
|
||||
--list-presets: list presets fastfetch knows about and exits. They can be loaded with --load-config. (+)
|
||||
--list-features: list the supported features fastfetch was compiled with and exits. Mainly for development.
|
||||
--print-logos: show available logos and exits
|
||||
--print-config: prints the default config and exits
|
||||
--print-structure: prints the default stucture and exits
|
||||
|
||||
General options:
|
||||
--structure <structure>: sets the structure of the fetch. Must be a colon separated list of keys. Use "fastfetch --list-modules" to see the ones available.
|
||||
--set <key=value>: hard set the value of a key
|
||||
-c <color>, --color <color>: sets the color of the keys. Must be a linux console color code or the name of a color (+)
|
||||
--color-[1-9] <color>: overwrites a color of the logo. Must be a linux console color code or the name of a color
|
||||
--spacing <width>: sets the distance between logo and text
|
||||
-s <str>, --separator <str>: sets the separator between key and value. Default is a colon with a space
|
||||
-x <offset>, --offsetx <offset>: sets the x offset. Can be negative to cut the logo, but no more than logo width.
|
||||
--show-errors <?value>: print occuring errors
|
||||
-r <?value> --recache <?value>: generate new cached values
|
||||
--nocache <?value>: don't use cached values, but also don't overwrite existing ones
|
||||
--print-remaining-logo <?value>: print the remaining logo, if it is higher than the number of lines shown
|
||||
--multithreading <?value>: use multiple threads to detect values
|
||||
--load-config <file>: load a config file (+)
|
||||
--allow-slow-operations <?value>: Allow operations that are usually very slow for more detailed output
|
||||
--disable-linewrap <?value>: Disable linewrap during the run
|
||||
--hide-cursor <?value>: Hide the cursor during the run
|
||||
--logo-raw <?value>: Print a custom logo as is, without any color replacements
|
||||
-r,--recache <?value>: generate new cached values
|
||||
--nocache <?value>: don't use cached values, but also don't overwrite existing ones
|
||||
--load-config <file>: load a config file or a preset (+)
|
||||
--multithreading <?value>: use multiple threads to detect values
|
||||
--allow-slow-operations <?value>: allow operations that are usually very slow for more detailed output
|
||||
|
||||
Logo options:
|
||||
-l <name>, --logo <name>: sets the shown logo. Also changes the main color accordingly. This will also load file contents as logo if the given argument is a valid path.
|
||||
--color-logo <?value>: if set to false, the logo will be black / white
|
||||
-l,--logo <logo>: set the logo to use. The type is specified by --logo-type. If default: the name of a builtin logo or a path to a file
|
||||
--logo-type <type>: set the type of the logo given. Must be auto, builtin, file, raw, sixel, kitty or chafa.
|
||||
--logo-width <width>: set the width of the logo (in characters), if it is an image
|
||||
--logo-height <height>: set the height of the logo (in characters), if it is an image
|
||||
--logo-color-[1-9] <color>: overwrite a color in the logo
|
||||
--logo-padding <padding>: set the padding on the left and the right of the logo
|
||||
--logo-padding-left <padding>: set the padding on the left of the logo
|
||||
--logo-padding-right <padding>: set the padding on the right of the logo
|
||||
--logo-print-remaining <?value>: weather to print the remaining logo, if it has more lines than modules to display
|
||||
--sixel <file>: short for --logo-type sixel --logo <file>
|
||||
--kitty <file>: short for --logo-type kitty --logo <file>
|
||||
--chafa <file>: short for --logo-type chafa --logo <file>
|
||||
--file <file>: short for --logo-type file --logo <file>
|
||||
--raw <file>: short for --logo-type raw --logo <file>
|
||||
|
||||
Display options:
|
||||
-s,--structure <structure>: sets the structure of the fetch. Must be a colon separated list of keys. Use "fastfetch --list-modules" to see the ones available.
|
||||
-c,--color <color>: sets the color of the keys. Must be a linux console color code or the name of a color (+)
|
||||
--separator <str>: sets the separator between key and value. Default is a colon with a space
|
||||
--set <key=value>: hard set the value of a key
|
||||
--show-errors <?value>: print occuring errors
|
||||
--disable-linewrap <?value>: weather to disable linewrap during the run
|
||||
--hide-cursor <?value>: weather to hide the cursor during the run
|
||||
|
||||
Format options: Provide the format string for custom output. Use fastfetch --help *-format for specific help.
|
||||
--os-format <format>
|
||||
@@ -113,7 +122,10 @@ Library options: Set the path of a library to load
|
||||
--lib-DConf <path>
|
||||
--lib-DBus <path>
|
||||
--lib-XFConf <path>
|
||||
--lib-RPM <path>
|
||||
--lib-sqlite3 <path>
|
||||
--lib-imagemagick <path>
|
||||
--lib-z <path>
|
||||
--lib-chafa <path>
|
||||
|
||||
Module specific options:
|
||||
--separator-string <str>: Set the string printed by the separator module
|
||||
|
||||
@@ -78,7 +78,7 @@ void ffdsConnectWayland(const FFinstance* instance, FFDisplayServerResult* resul
|
||||
if(getenv("XDG_RUNTIME_DIR") == NULL)
|
||||
return;
|
||||
|
||||
FF_LIBRARY_LOAD(wayland, instance->config.libWayland, , "libwayland-client.so", "libwayland-client.so.0")
|
||||
FF_LIBRARY_LOAD(wayland, instance->config.libWayland, , "libwayland-client.so", 1)
|
||||
|
||||
FF_LIBRARY_LOAD_SYMBOL(wayland, wl_display_connect,)
|
||||
FF_LIBRARY_LOAD_SYMBOL(wayland, wl_display_dispatch,)
|
||||
|
||||
@@ -98,7 +98,7 @@ static void getKDE(FFDisplayServerResult* result)
|
||||
{
|
||||
ffStrbufSetS(&result->deProcessName, "plasmashell");
|
||||
ffStrbufSetS(&result->dePrettyName, "KDE Plasma");
|
||||
ffParsePropFile("/usr/share/xsessions/plasma.desktop", "X-KDE-PluginInfo-Version =", &result->deVersion);
|
||||
ffParsePropFile(FASTFETCH_TARGET_DIR_USR"/share/xsessions/plasma.desktop", "X-KDE-PluginInfo-Version =", &result->deVersion);
|
||||
applyBetterWM(result, getenv("KDEWM"));
|
||||
}
|
||||
|
||||
@@ -106,14 +106,14 @@ static void getGnome(FFDisplayServerResult* result)
|
||||
{
|
||||
ffStrbufSetS(&result->deProcessName, "gnome-shell");
|
||||
ffStrbufSetS(&result->dePrettyName, "GNOME");
|
||||
ffParsePropFile("/usr/share/gnome-shell/org.gnome.Extensions", "version :", &result->deVersion);
|
||||
ffParsePropFile(FASTFETCH_TARGET_DIR_USR"/share/gnome-shell/org.gnome.Extensions", "version :", &result->deVersion);
|
||||
}
|
||||
|
||||
static void getCinnamon(FFDisplayServerResult* result)
|
||||
{
|
||||
ffStrbufSetS(&result->deProcessName, "cinnamon");
|
||||
ffStrbufSetS(&result->dePrettyName, "Cinnamon");
|
||||
ffParsePropFile("/usr/share/applications/cinnamon.desktop", "X-GNOME-Bugzilla-Version =", &result->deVersion);
|
||||
ffParsePropFile(FASTFETCH_TARGET_DIR_USR"/share/applications/cinnamon.desktop", "X-GNOME-Bugzilla-Version =", &result->deVersion);
|
||||
}
|
||||
|
||||
static void getMate(const FFinstance* instance, FFDisplayServerResult* result)
|
||||
@@ -130,7 +130,7 @@ static void getMate(const FFinstance* instance, FFDisplayServerResult* result)
|
||||
FFstrbuf micro;
|
||||
ffStrbufInit(µ);
|
||||
|
||||
ffParsePropFileValues("/usr/share/mate-about/mate-version.xml", 3, (FFpropquery[]) {
|
||||
ffParsePropFileValues(FASTFETCH_TARGET_DIR_USR"/share/mate-about/mate-version.xml", 3, (FFpropquery[]) {
|
||||
{"<platform>", &major},
|
||||
{"<minor>", &minor},
|
||||
{"<micro>", µ}
|
||||
@@ -159,7 +159,7 @@ static void getXFCE4(const FFinstance* instance, FFDisplayServerResult* result)
|
||||
{
|
||||
ffStrbufSetS(&result->deProcessName, "xfce4-session");
|
||||
ffStrbufSetS(&result->dePrettyName, "Xfce4");
|
||||
ffParsePropFile("/usr/share/gtk-doc/html/libxfce4ui/index.html", "<div><p class=\"releaseinfo\">Version", &result->deVersion);
|
||||
ffParsePropFile(FASTFETCH_TARGET_DIR_USR"/share/gtk-doc/html/libxfce4ui/index.html", "<div><p class=\"releaseinfo\">Version", &result->deVersion);
|
||||
|
||||
if(result->deVersion.length == 0 && instance->config.allowSlowOperations)
|
||||
{
|
||||
@@ -180,12 +180,12 @@ static void getLXQt(const FFinstance* instance, FFDisplayServerResult* result)
|
||||
{
|
||||
ffStrbufSetS(&result->deProcessName, "lxqt-session");
|
||||
ffStrbufSetS(&result->dePrettyName, "LXQt");
|
||||
ffParsePropFile("/usr/lib/pkgconfig/lxqt.pc", "Version:", &result->deVersion);
|
||||
ffParsePropFile(FASTFETCH_TARGET_DIR_USR"/lib/pkgconfig/lxqt.pc", "Version:", &result->deVersion);
|
||||
|
||||
if(result->deVersion.length == 0)
|
||||
ffParsePropFile("/usr/share/cmake/lxqt/lxqt-config.cmake", "set ( LXQT_VERSION", &result->deVersion);
|
||||
ffParsePropFile(FASTFETCH_TARGET_DIR_USR"/share/cmake/lxqt/lxqt-config.cmake", "set ( LXQT_VERSION", &result->deVersion);
|
||||
if(result->deVersion.length == 0)
|
||||
ffParsePropFile("/usr/share/cmake/lxqt/lxqt-config-version.cmake", "set ( PACKAGE_VERSION", &result->deVersion);
|
||||
ffParsePropFile(FASTFETCH_TARGET_DIR_USR"/share/cmake/lxqt/lxqt-config-version.cmake", "set ( PACKAGE_VERSION", &result->deVersion);
|
||||
|
||||
if(result->deVersion.length == 0 && instance->config.allowSlowOperations)
|
||||
{
|
||||
@@ -197,7 +197,7 @@ static void getLXQt(const FFinstance* instance, FFDisplayServerResult* result)
|
||||
});
|
||||
|
||||
result->deVersion.length = 0; //don't set '\0' byte
|
||||
ffGetPropValueFromLines(result->deVersion.chars , "liblxqt", &result->deVersion);
|
||||
ffParsePropLines(result->deVersion.chars , "liblxqt", &result->deVersion);
|
||||
}
|
||||
|
||||
FFstrbuf wmProcessNameBuffer;
|
||||
|
||||
@@ -89,7 +89,7 @@ static void xcbDetectWMfromEWMH(XcbPropertyData* data, xcb_connection_t* connect
|
||||
|
||||
void ffdsConnectXcb(const FFinstance* instance, FFDisplayServerResult* result)
|
||||
{
|
||||
FF_LIBRARY_LOAD(xcb, instance->config.libXcb, , "libxcb.so", "libxcb.so.1")
|
||||
FF_LIBRARY_LOAD(xcb, instance->config.libXcb, , "libxcb.so", 2)
|
||||
FF_LIBRARY_LOAD_SYMBOL(xcb, xcb_connect,)
|
||||
FF_LIBRARY_LOAD_SYMBOL(xcb, xcb_get_setup,)
|
||||
FF_LIBRARY_LOAD_SYMBOL(xcb, xcb_setup_roots_iterator,)
|
||||
@@ -213,15 +213,15 @@ static bool xcbRandrHandleCrtc(XcbRandrData* data, xcb_randr_crtc_t crtc)
|
||||
return false;
|
||||
|
||||
bool res = xcbRandrHandleMode(data, crtcInfoReply->mode);
|
||||
|
||||
free(crtcInfoReply);
|
||||
|
||||
return res ? true : ffdsAppendResolution(
|
||||
res = res ? true : ffdsAppendResolution(
|
||||
data->result,
|
||||
(uint32_t) crtcInfoReply->width,
|
||||
(uint32_t) crtcInfoReply->height,
|
||||
data->defaultRefreshRate
|
||||
);
|
||||
|
||||
free(crtcInfoReply);
|
||||
return res;
|
||||
}
|
||||
|
||||
static bool xcbRandrHandleOutput(XcbRandrData* data, xcb_randr_output_t output)
|
||||
@@ -324,7 +324,7 @@ static void xcbRandrHandleScreen(XcbRandrData* data, xcb_screen_t* screen)
|
||||
|
||||
void ffdsConnectXcbRandr(const FFinstance* instance, FFDisplayServerResult* result)
|
||||
{
|
||||
FF_LIBRARY_LOAD(xcbRandr, instance->config.libXcbRandr, , "libxcb-randr.so", "libxcb-randr.so.0")
|
||||
FF_LIBRARY_LOAD(xcbRandr, instance->config.libXcbRandr, , "libxcb-randr.so", 1)
|
||||
FF_LIBRARY_LOAD_SYMBOL(xcbRandr, xcb_connect,)
|
||||
FF_LIBRARY_LOAD_SYMBOL(xcbRandr, xcb_get_setup,)
|
||||
FF_LIBRARY_LOAD_SYMBOL(xcbRandr, xcb_setup_roots_iterator,)
|
||||
|
||||
@@ -53,7 +53,7 @@ static void x11DetectWMFromEWMH(X11PropertyData* data, Display* display, FFDispl
|
||||
|
||||
void ffdsConnectXlib(const FFinstance* instance, FFDisplayServerResult* result)
|
||||
{
|
||||
FF_LIBRARY_LOAD(x11, instance->config.libX11, , "libX11.so", "libX11.so.6", "libX11-xcb.so", "libX11-xcb.so.1")
|
||||
FF_LIBRARY_LOAD(x11, instance->config.libX11, , "libX11.so", 7, "libX11-xcb.so", 2)
|
||||
FF_LIBRARY_LOAD_SYMBOL(x11, XOpenDisplay,)
|
||||
FF_LIBRARY_LOAD_SYMBOL(x11, XCloseDisplay,)
|
||||
|
||||
@@ -161,15 +161,15 @@ static bool xrandrHandleCrtc(XrandrData* data, RRCrtc crtc)
|
||||
return false;
|
||||
|
||||
bool res = xrandrHandleMode(data, crtcInfo->mode);
|
||||
|
||||
data->ffXRRFreeCrtcInfo(crtcInfo);
|
||||
|
||||
return res ? true : ffdsAppendResolution(
|
||||
res = res ? true : ffdsAppendResolution(
|
||||
data->result,
|
||||
(uint32_t) crtcInfo->width,
|
||||
(uint32_t) crtcInfo->height,
|
||||
data->defaultRefreshRate
|
||||
);
|
||||
|
||||
data->ffXRRFreeCrtcInfo(crtcInfo);
|
||||
return res;
|
||||
}
|
||||
|
||||
static bool xrandrHandleOutput(XrandrData* data, RROutput output)
|
||||
@@ -257,7 +257,7 @@ static void xrandrHandleScreen(XrandrData* data, Screen* screen)
|
||||
|
||||
void ffdsConnectXrandr(const FFinstance* instance, FFDisplayServerResult* result)
|
||||
{
|
||||
FF_LIBRARY_LOAD(xrandr, instance->config.libXrandr, , "libXrandr.so", "libXrandr.so.2")
|
||||
FF_LIBRARY_LOAD(xrandr, instance->config.libXrandr, , "libXrandr.so", 3)
|
||||
|
||||
FF_LIBRARY_LOAD_SYMBOL(xrandr, XOpenDisplay,)
|
||||
FF_LIBRARY_LOAD_SYMBOL(xrandr, XCloseDisplay,)
|
||||
|
||||
+17
-53
@@ -69,23 +69,14 @@ static void detectGTKFromDConf(FFinstance* instance, FFGTKResult* result)
|
||||
cursorTheme = ffSettingsGet(instance, "/org/mate/peripherals-mouse/cursor-theme", "org.mate.peripherals-mouse", NULL, "cursor-theme", FF_VARIANT_TYPE_STRING).strValue;
|
||||
cursorSize = ffSettingsGet(instance, "/org/mate/peripherals-mouse/cursor-size", "org.mate.peripherals-mouse", NULL, "cursor-size", FF_VARIANT_TYPE_INT).intValue;
|
||||
}
|
||||
|
||||
//Fallback + Gnome impl
|
||||
|
||||
if(themeName == NULL)
|
||||
else if(ffStrbufIgnCaseCompS(&wmde->dePrettyName, "Gnome") == 0)
|
||||
{
|
||||
themeName = ffSettingsGet(instance, "/org/gnome/desktop/interface/gtk-theme", "org.gnome.desktop.interface", NULL, "gtk-theme", FF_VARIANT_TYPE_STRING).strValue;
|
||||
|
||||
if(iconsName == NULL)
|
||||
iconsName = ffSettingsGet(instance, "/org/gnome/desktop/interface/icon-theme", "org.gnome.desktop.interface", NULL, "icon-theme", FF_VARIANT_TYPE_STRING).strValue;
|
||||
|
||||
if(fontName == NULL)
|
||||
fontName = ffSettingsGet(instance, "/org/gnome/desktop/interface/font-name", "org.gnome.desktop.interface", NULL, "font-name", FF_VARIANT_TYPE_STRING).strValue;
|
||||
|
||||
if(cursorTheme == NULL)
|
||||
cursorTheme = ffSettingsGet(instance, "/org/gnome/desktop/interface/cursor-theme", "org.gnome.desktop.interface", NULL, "cursor-theme", FF_VARIANT_TYPE_STRING).strValue;
|
||||
|
||||
if(cursorSize <= 0)
|
||||
cursorSize = ffSettingsGet(instance, "/org/gnome/desktop/interface/cursor-size", "org.gnome.desktop.interface", NULL, "cursor-size", FF_VARIANT_TYPE_INT).intValue;
|
||||
}
|
||||
|
||||
pthread_mutex_unlock(&mutex);
|
||||
applyGTKDConfSettings(result, themeName, iconsName, fontName, cursorTheme, cursorSize);
|
||||
@@ -146,41 +137,8 @@ static void detectGTKFromConfigDir(FFstrbuf* configDir, const char* version, FFG
|
||||
ffStrbufSubstrBefore(configDir, configDirLength);
|
||||
}
|
||||
|
||||
static void detectGTK(FFinstance* instance, const char* version, const char* envVariable, FFGTKResult* result)
|
||||
static void detectGTK(FFinstance* instance, const char* version, FFGTKResult* result)
|
||||
{
|
||||
FFstrbuf buffer;
|
||||
ffStrbufInitA(&buffer, 128);
|
||||
|
||||
// From ENV: GTK*_RC_FILES
|
||||
|
||||
ffStrbufSetS(&buffer, getenv(envVariable));
|
||||
uint32_t startIndex = 0;
|
||||
while (startIndex < buffer.length)
|
||||
{
|
||||
uint32_t colonIndex = ffStrbufNextIndexC(&buffer, startIndex, ':');
|
||||
buffer.chars[colonIndex] = '\0';
|
||||
|
||||
detectGTKFromConfigFile(buffer.chars + startIndex, result);
|
||||
if(allPropertiesSet(result))
|
||||
{
|
||||
ffStrbufDestroy(&buffer);
|
||||
return;
|
||||
}
|
||||
|
||||
startIndex = colonIndex + 1;
|
||||
}
|
||||
|
||||
//From DConf / GSettings
|
||||
|
||||
detectGTKFromDConf(instance, result);
|
||||
if(allPropertiesSet(result))
|
||||
{
|
||||
ffStrbufDestroy(&buffer);
|
||||
return;
|
||||
}
|
||||
|
||||
//From config dirs
|
||||
|
||||
//We need to do this because we use multiple threads on configDirs
|
||||
FFstrbuf baseDirCopy;
|
||||
ffStrbufInitA(&baseDirCopy, 64);
|
||||
@@ -191,14 +149,20 @@ static void detectGTK(FFinstance* instance, const char* version, const char* env
|
||||
ffStrbufSet(&baseDirCopy, baseDir);
|
||||
detectGTKFromConfigDir(&baseDirCopy, version, result);
|
||||
if(allPropertiesSet(result))
|
||||
break;
|
||||
{
|
||||
ffStrbufDestroy(&baseDirCopy);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
ffStrbufDestroy(&baseDirCopy);
|
||||
ffStrbufDestroy(&buffer);
|
||||
|
||||
//Mate, Cinnamon and Gnome use dconf to save theme config
|
||||
//On other DEs, this will do nothing
|
||||
detectGTKFromDConf(instance, result);
|
||||
}
|
||||
|
||||
#define FF_CALCULATE_GTK_IMPL(version) \
|
||||
#define FF_DETECT_GTK_IMPL(version) \
|
||||
static pthread_mutex_t mutex = PTHREAD_MUTEX_INITIALIZER; \
|
||||
static FFGTKResult result; \
|
||||
static bool init = false; \
|
||||
@@ -213,23 +177,23 @@ static void detectGTK(FFinstance* instance, const char* version, const char* env
|
||||
ffStrbufInit(&result.font); \
|
||||
ffStrbufInit(&result.cursor); \
|
||||
ffStrbufInit(&result.cursorSize); \
|
||||
detectGTK(instance, #version, "GTK"#version"_RC_FILES", &result); \
|
||||
detectGTK(instance, #version, &result); \
|
||||
pthread_mutex_unlock(&mutex); \
|
||||
return &result;
|
||||
|
||||
const FFGTKResult* ffDetectGTK2(FFinstance* instance)
|
||||
{
|
||||
FF_CALCULATE_GTK_IMPL(2)
|
||||
FF_DETECT_GTK_IMPL(2)
|
||||
}
|
||||
|
||||
const FFGTKResult* ffDetectGTK3(FFinstance* instance)
|
||||
{
|
||||
FF_CALCULATE_GTK_IMPL(3)
|
||||
FF_DETECT_GTK_IMPL(3)
|
||||
}
|
||||
|
||||
const FFGTKResult* ffDetectGTK4(FFinstance* instance)
|
||||
{
|
||||
FF_CALCULATE_GTK_IMPL(4)
|
||||
FF_DETECT_GTK_IMPL(4)
|
||||
}
|
||||
|
||||
#undef FF_CALCULATE_GTK_IMPL
|
||||
|
||||
+8
-37
@@ -1,10 +1,10 @@
|
||||
#include "fastfetch.h"
|
||||
|
||||
#include <ctype.h>
|
||||
#include <string.h>
|
||||
#include <pthread.h>
|
||||
|
||||
#define FF_DBUS_MPRIS_PREFIX "org.mpris.MediaPlayer2."
|
||||
#define FF_DBUS_TIMEOUT_MILLISECONDS 35
|
||||
|
||||
#ifdef FF_HAVE_DBUS
|
||||
#include <dbus/dbus.h>
|
||||
@@ -69,8 +69,11 @@ static bool getValue(DBusMessageIter* iter, FFstrbuf* result, DBusData* data)
|
||||
|
||||
while(true)
|
||||
{
|
||||
if((foundAValue = getValue(&subIter, result, data)))
|
||||
if(getValue(&subIter, result, data))
|
||||
{
|
||||
foundAValue = true;
|
||||
ffStrbufAppendS(result, ", ");
|
||||
}
|
||||
|
||||
FF_DBUS_ITER_CONTINUE(subIter);
|
||||
}
|
||||
@@ -84,7 +87,7 @@ static bool getValue(DBusMessageIter* iter, FFstrbuf* result, DBusData* data)
|
||||
static DBusMessage* getReply(DBusMessage* request, DBusData* data)
|
||||
{
|
||||
DBusPendingCall* pendingCall = NULL;
|
||||
dbus_bool_t succesfull = data->ffdbus_connection_send_with_reply(data->connection, request, &pendingCall, DBUS_TIMEOUT_USE_DEFAULT);
|
||||
dbus_bool_t succesfull = data->ffdbus_connection_send_with_reply(data->connection, request, &pendingCall, FF_DBUS_TIMEOUT_MILLISECONDS);
|
||||
data->ffdbus_message_unref(request);
|
||||
if(!succesfull || pendingCall == NULL)
|
||||
return NULL;
|
||||
@@ -209,6 +212,7 @@ static bool getBusProperties(const char* busName, FFMediaResult* result, DBusDat
|
||||
{
|
||||
ffStrbufClear(&result->artist);
|
||||
ffStrbufClear(&result->album);
|
||||
ffStrbufClear(&result->url);
|
||||
return false;
|
||||
}
|
||||
|
||||
@@ -285,7 +289,7 @@ static void getMedia(FFinstance* instance, FFMediaResult* result)
|
||||
{
|
||||
DBusData data;
|
||||
|
||||
FF_LIBRARY_LOAD(dbus, instance->config.libDBus, , "libdbus-1.so", "libdbus-1.so.3");
|
||||
FF_LIBRARY_LOAD(dbus, instance->config.libDBus, , "libdbus-1.so", 4);
|
||||
FF_LIBRARY_LOAD_SYMBOL(dbus, dbus_bus_get,)
|
||||
FF_LIBRARY_LOAD_SYMBOL_ADRESS(dbus, data.ffdbus_message_new_method_call, dbus_message_new_method_call,)
|
||||
FF_LIBRARY_LOAD_SYMBOL_ADRESS(dbus, data.ffdbus_message_iter_init, dbus_message_iter_init,)
|
||||
@@ -316,28 +320,6 @@ static void getMedia(FFinstance* instance, FFMediaResult* result)
|
||||
getBestBus(result, &data);
|
||||
|
||||
dlclose(dbus);
|
||||
|
||||
//If we are on a website, prepend the website name
|
||||
if(ffStrbufStartsWithS(&result->url, "https://www."))
|
||||
ffStrbufAppendS(&result->playerPretty, result->url.chars + 12);
|
||||
else if(ffStrbufStartsWithS(&result->url, "http://www."))
|
||||
ffStrbufAppendS(&result->playerPretty, result->url.chars + 11);
|
||||
else if(ffStrbufStartsWithS(&result->url, "https://"))
|
||||
ffStrbufAppendS(&result->playerPretty, result->url.chars + 8);
|
||||
else if(ffStrbufStartsWithS(&result->url, "http://"))
|
||||
ffStrbufAppendS(&result->playerPretty, result->url.chars + 7);
|
||||
|
||||
//If we found a website name, make it more pretty
|
||||
if(result->playerPretty.length > 0)
|
||||
{
|
||||
ffStrbufSubstrBeforeFirstC(&result->playerPretty, '/'); //Remove the path
|
||||
ffStrbufSubstrBeforeLastC(&result->playerPretty, '.'); //Remove the TLD
|
||||
}
|
||||
|
||||
//Check again for length, as we may have removed everything.
|
||||
//If we don't have subdomains, it is usually more pretty to capitalize the first letter.
|
||||
if(result->playerPretty.length > 0 && ffStrbufFirstIndexC(&result->playerPretty, '.') < result->playerPretty.length)
|
||||
result->playerPretty.chars[0] = (char) toupper(result->playerPretty.chars[0]);
|
||||
}
|
||||
|
||||
#endif
|
||||
@@ -358,7 +340,6 @@ const FFMediaResult* ffDetectMedia(FFinstance* instance)
|
||||
|
||||
ffStrbufInit(&result.busNameShort);
|
||||
ffStrbufInit(&result.player);
|
||||
ffStrbufInit(&result.playerPretty);
|
||||
ffStrbufInit(&result.song);
|
||||
ffStrbufInit(&result.artist);
|
||||
ffStrbufInit(&result.album);
|
||||
@@ -381,16 +362,6 @@ const FFMediaResult* ffDetectMedia(FFinstance* instance)
|
||||
if(result.player.length == 0)
|
||||
ffStrbufAppend(&result.player, &result.busNameShort);
|
||||
|
||||
bool hasCustomPrettyName = result.playerPretty.length > 0;
|
||||
|
||||
if(hasCustomPrettyName)
|
||||
ffStrbufAppendS(&result.playerPretty, " (");
|
||||
|
||||
ffStrbufAppend(&result.playerPretty, &result.player);
|
||||
|
||||
if(hasCustomPrettyName)
|
||||
ffStrbufAppendC(&result.playerPretty, ')');
|
||||
|
||||
pthread_mutex_unlock(&mutex);
|
||||
return &result;
|
||||
}
|
||||
|
||||
+70
-4
@@ -1,5 +1,6 @@
|
||||
#include "fastfetch.h"
|
||||
|
||||
#include <string.h>
|
||||
#include <pthread.h>
|
||||
|
||||
#if !defined(__ANDROID__)
|
||||
@@ -26,9 +27,70 @@ static void parseFile(const char* fileName, FFOSResult* result)
|
||||
});
|
||||
}
|
||||
|
||||
static void getUbuntuFlavour(FFOSResult* result)
|
||||
{
|
||||
const char* xdgConfigDirs = getenv("XDG_CONFIG_DIRS");
|
||||
if(!ffStrSet(xdgConfigDirs))
|
||||
return;
|
||||
|
||||
if(strstr(xdgConfigDirs, "kde") != NULL || strstr(xdgConfigDirs, "plasma") != NULL)
|
||||
{
|
||||
ffStrbufSetS(&result->name, "Kubuntu");
|
||||
ffStrbufSetS(&result->prettyName, "Kubuntu");
|
||||
ffStrbufSetS(&result->id, "kubuntu");
|
||||
ffStrbufSetS(&result->idLike, "ubuntu");
|
||||
return;
|
||||
}
|
||||
|
||||
if(strstr(xdgConfigDirs, "xfce") != NULL || strstr(xdgConfigDirs, "xubuntu") != NULL)
|
||||
{
|
||||
ffStrbufSetS(&result->name, "Xubuntu");
|
||||
ffStrbufSetS(&result->prettyName, "Xubuntu");
|
||||
ffStrbufSetS(&result->id, "xubuntu");
|
||||
ffStrbufSetS(&result->idLike, "ubuntu");
|
||||
return;
|
||||
}
|
||||
|
||||
if(strstr(xdgConfigDirs, "lxde") != NULL || strstr(xdgConfigDirs, "lubuntu") != NULL)
|
||||
{
|
||||
ffStrbufSetS(&result->name, "Lubuntu");
|
||||
ffStrbufSetS(&result->prettyName, "Lubuntu");
|
||||
ffStrbufSetS(&result->id, "lubuntu");
|
||||
ffStrbufSetS(&result->idLike, "ubuntu");
|
||||
return;
|
||||
}
|
||||
|
||||
if(strstr(xdgConfigDirs, "budgie") != NULL)
|
||||
{
|
||||
ffStrbufSetS(&result->name, "Ubuntu Budgie");
|
||||
ffStrbufSetS(&result->prettyName, "Ubuntu Budgie");
|
||||
ffStrbufSetS(&result->id, "ubuntu-budgie");
|
||||
ffStrbufSetS(&result->idLike, "ubuntu");
|
||||
return;
|
||||
}
|
||||
|
||||
if(strstr(xdgConfigDirs, "mate") != NULL)
|
||||
{
|
||||
ffStrbufSetS(&result->name, "Ubuntu MATE");
|
||||
ffStrbufSetS(&result->prettyName, "Ubuntu MATE");
|
||||
ffStrbufSetS(&result->id, "ubuntu-mate");
|
||||
ffStrbufSetS(&result->idLike, "ubuntu");
|
||||
return;
|
||||
}
|
||||
|
||||
if(strstr(xdgConfigDirs, "studio") != NULL)
|
||||
{
|
||||
ffStrbufSetS(&result->name, "Ubuntu Studio");
|
||||
ffStrbufSetS(&result->prettyName, "Ubuntu Studio");
|
||||
ffStrbufSetS(&result->id, "ubuntu-studio");
|
||||
ffStrbufSetS(&result->idLike, "ubuntu");
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
const FFOSResult* ffDetectOS(FFinstance* instance)
|
||||
const FFOSResult* ffDetectOS(const FFinstance* instance)
|
||||
{
|
||||
static pthread_mutex_t mutex = PTHREAD_MUTEX_INITIALIZER;
|
||||
static FFOSResult result;
|
||||
@@ -64,10 +126,14 @@ const FFOSResult* ffDetectOS(FFinstance* instance)
|
||||
}
|
||||
else
|
||||
{
|
||||
parseFile("/etc/os-release", &result);
|
||||
parseFile("/usr/lib/os-release", &result);
|
||||
parseFile("/etc/lsb-release", &result);
|
||||
parseFile(FASTFETCH_TARGET_DIR_ROOT"/etc/os-release", &result);
|
||||
parseFile(FASTFETCH_TARGET_DIR_USR"/lib/os-release", &result);
|
||||
parseFile(FASTFETCH_TARGET_DIR_ROOT"/etc/lsb-release", &result);
|
||||
}
|
||||
|
||||
if(ffStrbufIgnCaseCompS(&result.id, "ubuntu") == 0)
|
||||
getUbuntuFlavour(&result);
|
||||
|
||||
#else
|
||||
ffStrbufSetS(&result.name, "Android");
|
||||
ffStrbufSetS(&result.id, "android");
|
||||
|
||||
@@ -42,20 +42,20 @@ static bool detectFromConfigFile(const FFstrbuf* filename, FFPlasmaResult* resul
|
||||
}
|
||||
|
||||
if(category == PLASMA_CATEGORY_KDE && result->widgetStyle.length == 0)
|
||||
ffGetPropValue(line, "widgetStyle =", &result->widgetStyle);
|
||||
ffParsePropLine(line, "widgetStyle =", &result->widgetStyle);
|
||||
else if(category == PLASMA_CATEGORY_ICONS && result->icons.length == 0)
|
||||
ffGetPropValue(line, "Theme =", &result->icons);
|
||||
ffParsePropLine(line, "Theme =", &result->icons);
|
||||
else if(category == PLASMA_CATEGORY_GENERAL)
|
||||
{
|
||||
if(result->colorScheme.length == 0)
|
||||
ffGetPropValue(line, "ColorScheme =", &result->colorScheme);
|
||||
ffParsePropLine(line, "ColorScheme =", &result->colorScheme);
|
||||
|
||||
if(result->font.length == 0)
|
||||
ffGetPropValue(line, "font =", &result->font);
|
||||
ffParsePropLine(line, "font =", &result->font);
|
||||
|
||||
//Before plasma 5.23, "Font" was the key instead of "font". Since a lot of distros ship older versions, we test for both.
|
||||
if(result->font.length == 0)
|
||||
ffGetPropValue(line, "Font =", &result->font);
|
||||
ffParsePropLine(line, "Font =", &result->font);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
+188
-121
@@ -12,8 +12,6 @@ typedef struct FFdata
|
||||
{
|
||||
FFvaluestore valuestore;
|
||||
FFstrbuf structure;
|
||||
FFstrbuf logoName;
|
||||
FFstrbuf logoColors[FASTFETCH_LOGO_MAX_COLORS];
|
||||
bool multithreading;
|
||||
} FFdata;
|
||||
|
||||
@@ -312,7 +310,8 @@ static inline void printCommandHelp(const char* command)
|
||||
}
|
||||
else if(strcasecmp(command, "song-format") == 0)
|
||||
{
|
||||
constructAndPrintCommandHelpFormat("song", "{2} - {3} - {1}", 4,
|
||||
constructAndPrintCommandHelpFormat("song", "{3} - {4} - {1}", 5,
|
||||
"Song name pretty",
|
||||
"Song name",
|
||||
"Artist name",
|
||||
"Album name",
|
||||
@@ -394,7 +393,7 @@ static inline void listAvailablePresets(FFinstance* instance)
|
||||
ffStrbufAppendS(&folder, "/.local/share/fastfetch/presets/");
|
||||
listAvailablePresetsFromFolder(&folder, 0, NULL);
|
||||
|
||||
ffStrbufSetS(&folder, "/usr/share/fastfetch/presets/");
|
||||
ffStrbufSetS(&folder, FASTFETCH_TARGET_DIR_USR"/share/fastfetch/presets/");
|
||||
listAvailablePresetsFromFolder(&folder, 0, NULL);
|
||||
|
||||
ffStrbufDestroy(&folder);
|
||||
@@ -489,7 +488,7 @@ static void optionParseConfigFile(FFinstance* instance, FFdata* data, const char
|
||||
return;
|
||||
}
|
||||
|
||||
ffStrbufSetS(&filename, "/usr/share/fastfetch/presets/");
|
||||
ffStrbufSetS(&filename, FASTFETCH_TARGET_DIR_USR"/share/fastfetch/presets/");
|
||||
ffStrbufAppendS(&filename, value);
|
||||
|
||||
file = fopen(filename.chars, "r");
|
||||
@@ -607,8 +606,31 @@ static void optionParseColor(const char* key, const char* value, FFstrbuf* buffe
|
||||
}
|
||||
}
|
||||
|
||||
static uint32_t optionParseUInt32(const char* key, const char* value)
|
||||
{
|
||||
if(value == NULL)
|
||||
{
|
||||
fprintf(stderr, "Error: usage: %s <num>\n", key);
|
||||
exit(480);
|
||||
}
|
||||
|
||||
char* end;
|
||||
uint32_t num = (uint32_t) strtoul(value, &end, 10);
|
||||
if(*end != '\0')
|
||||
{
|
||||
fprintf(stderr, "Error: usage: %s <num>\n", key);
|
||||
exit(479);
|
||||
}
|
||||
|
||||
return num;
|
||||
}
|
||||
|
||||
static void parseOption(FFinstance* instance, FFdata* data, const char* key, const char* value)
|
||||
{
|
||||
///////////////////////
|
||||
//Informative options//
|
||||
///////////////////////
|
||||
|
||||
if(strcasecmp(key, "-h") == 0 || strcasecmp(key, "--help") == 0)
|
||||
{
|
||||
printCommandHelp(value);
|
||||
@@ -616,7 +638,7 @@ static void parseOption(FFinstance* instance, FFdata* data, const char* key, con
|
||||
}
|
||||
else if(strcasecmp(key, "-v") == 0 || strcasecmp(key, "--version") == 0)
|
||||
{
|
||||
puts("fastfetch "FASTFETCH_PROJECT_VERSION""FASTFETCH_PROJECT_VERSION_EXTRA);
|
||||
puts("fastfetch "FASTFETCH_PROJECT_VERSION""FASTFETCH_PROJECT_VERSION_TWEAK);
|
||||
exit(0);
|
||||
}
|
||||
else if(strcasecmp(key, "--version-raw") == 0)
|
||||
@@ -624,21 +646,6 @@ static void parseOption(FFinstance* instance, FFdata* data, const char* key, con
|
||||
puts(FASTFETCH_PROJECT_VERSION);
|
||||
exit(0);
|
||||
}
|
||||
else if(strcasecmp(key, "--list-logos") == 0)
|
||||
{
|
||||
ffListLogos();
|
||||
exit(0);
|
||||
}
|
||||
else if(strcasecmp(key, "--list-logos-autocompletion") == 0)
|
||||
{
|
||||
ffListLogosForAutocompletion();
|
||||
exit(0);
|
||||
}
|
||||
else if(strcasecmp(key, "--print-logos") == 0)
|
||||
{
|
||||
ffPrintLogos(instance);
|
||||
exit(0);
|
||||
}
|
||||
else if(strcasecmp(key, "--print-config") == 0)
|
||||
{
|
||||
fputs(FASTFETCH_DATATEXT_CONFIG, stdout);
|
||||
@@ -664,32 +671,150 @@ static void parseOption(FFinstance* instance, FFdata* data, const char* key, con
|
||||
ffListFeatures();
|
||||
exit(0);
|
||||
}
|
||||
else if(strcasecmp(key, "--spacing") == 0)
|
||||
else if(strcasecmp(key, "--list-logos") == 0)
|
||||
{
|
||||
ffListBuiltinLogos();
|
||||
exit(0);
|
||||
}
|
||||
else if(strcasecmp(key, "--list-logos-autocompletion") == 0)
|
||||
{
|
||||
ffListBuiltinLogosAutocompletion();
|
||||
exit(0);
|
||||
}
|
||||
else if(strcasecmp(key, "--print-logos") == 0)
|
||||
{
|
||||
ffPrintBuiltinLogos(instance);
|
||||
exit(0);
|
||||
}
|
||||
|
||||
///////////////////
|
||||
//General options//
|
||||
///////////////////
|
||||
|
||||
else if(strcasecmp(key, "-r") == 0 || strcasecmp(key, "--recache") == 0)
|
||||
{
|
||||
//Set cacheSave as well, because the user expects the values to be cached when expliciting using --recache
|
||||
instance->config.recache = optionParseBoolean(value);
|
||||
instance->config.cacheSave = instance->config.recache;
|
||||
}
|
||||
else if(strcasecmp(key, "--nocache") == 0)
|
||||
{
|
||||
instance->config.recache = optionParseBoolean(value);
|
||||
instance->config.cacheSave = false;
|
||||
}
|
||||
else if(strcasecmp(key, "--load-config") == 0)
|
||||
optionParseConfigFile(instance, data, key, value);
|
||||
else if(strcasecmp(key, "--multithreading") == 0)
|
||||
data->multithreading = optionParseBoolean(value);
|
||||
else if(strcasecmp(key, "--allow-slow-operations") == 0)
|
||||
instance->config.allowSlowOperations = optionParseBoolean(value);
|
||||
|
||||
////////////////
|
||||
//Logo options//
|
||||
////////////////
|
||||
|
||||
else if(strcasecmp(key, "-l") == 0 || strcasecmp(key, "--logo") == 0)
|
||||
optionParseString(key, value, &instance->config.logoSource);
|
||||
else if(strcasecmp(key, "--logo-type") == 0)
|
||||
{
|
||||
if(value == NULL)
|
||||
{
|
||||
fprintf(stderr, "Error: usage: %s <width>\n", key);
|
||||
exit(404);
|
||||
fprintf(stderr, "Error: usage: %s <type>\n", key);
|
||||
exit(476);
|
||||
}
|
||||
if(sscanf(value, "%hu", &instance->config.logoKeySpacing) != 1)
|
||||
|
||||
if(strcasecmp(value, "auto") == 0)
|
||||
instance->config.logoType = FF_LOGO_TYPE_AUTO;
|
||||
else if(strcasecmp(value, "builtin") == 0)
|
||||
instance->config.logoType = FF_LOGO_TYPE_BUILTIN;
|
||||
else if(strcasecmp(value, "file") == 0)
|
||||
instance->config.logoType = FF_LOGO_TYPE_FILE;
|
||||
else if(strcasecmp(value, "raw") == 0)
|
||||
instance->config.logoType = FF_LOGO_TYPE_RAW;
|
||||
else if(strcasecmp(value, "sixel") == 0)
|
||||
instance->config.logoType = FF_LOGO_TYPE_SIXEL;
|
||||
else if(strcasecmp(value, "kitty") == 0)
|
||||
instance->config.logoType = FF_LOGO_TYPE_KITTY;
|
||||
else if(strcasecmp(value, "chafa") == 0)
|
||||
instance->config.logoType = FF_LOGO_TYPE_CHAFA;
|
||||
else
|
||||
{
|
||||
fprintf(stderr, "Error: couldn't parse %s to uint16_t\n", value);
|
||||
exit(405);
|
||||
fprintf(stderr, "Error: unknown logo type: %s\n", value);
|
||||
exit(478);
|
||||
}
|
||||
}
|
||||
else if(strcasecmp(key, "-x") == 0 || strcasecmp(key, "--offsetx") == 0)
|
||||
else if(strncasecmp(key, "--logo-color-", 13) == 0 && key[13] != '\0' && key[14] == '\0') // matches "--logo-color-*"
|
||||
{
|
||||
if(value == NULL)
|
||||
//Map the number to an array index, so that '1' -> 0, '2' -> 1, etc.
|
||||
int index = (int)key[13] - 49;
|
||||
|
||||
//Match only --logo-color-[1-9]
|
||||
if(index < 0 || index >= FASTFETCH_LOGO_MAX_COLORS)
|
||||
{
|
||||
fprintf(stderr, "Error: usage: %s <offset>\n", key);
|
||||
exit(408);
|
||||
}
|
||||
if(sscanf(value, "%hi", &instance->config.offsetx) != 1)
|
||||
{
|
||||
fprintf(stderr, "Error: couldn't parse %s to int16_t\n", value);
|
||||
exit(409);
|
||||
fprintf(stderr, "Error: invalid --color-[1-9] index: %c\n", key[13]);
|
||||
exit(472);
|
||||
}
|
||||
|
||||
optionParseColor(key, value, &instance->config.logoColors[index]);
|
||||
}
|
||||
else if(strcasecmp(key, "--logo-width") == 0)
|
||||
instance->config.logoWidth = optionParseUInt32(key, value);
|
||||
else if(strcasecmp(key, "--logo-height") == 0)
|
||||
instance->config.logoHeight = optionParseUInt32(key, value);
|
||||
else if(strcasecmp(key, "--logo-padding") == 0)
|
||||
{
|
||||
uint32_t padding = optionParseUInt32(key, value);
|
||||
instance->config.logoPaddingLeft = padding;
|
||||
instance->config.logoPaddingRight = padding;
|
||||
}
|
||||
else if(strcasecmp(key, "--logo-padding-left") == 0)
|
||||
instance->config.logoPaddingLeft = optionParseUInt32(key, value);
|
||||
else if(strcasecmp(key, "--logo-padding-right") == 0)
|
||||
instance->config.logoPaddingRight = optionParseUInt32(key, value);
|
||||
else if(strcasecmp(key, "--logo-print-remaining") == 0)
|
||||
instance->config.logoPrintRemaining = optionParseBoolean(value);
|
||||
else if(strcasecmp(key, "--sixel") == 0)
|
||||
{
|
||||
optionParseString(key, value, &instance->config.logoSource);
|
||||
instance->config.logoType = FF_LOGO_TYPE_SIXEL;
|
||||
}
|
||||
else if(strcasecmp(key, "--kitty") == 0)
|
||||
{
|
||||
optionParseString(key, value, &instance->config.logoSource);
|
||||
instance->config.logoType = FF_LOGO_TYPE_KITTY;
|
||||
}
|
||||
else if(strcasecmp(key, "--file") == 0)
|
||||
{
|
||||
optionCheckString(key, value, &instance->config.logoSource);
|
||||
instance->config.logoType = FF_LOGO_TYPE_FILE;
|
||||
}
|
||||
else if(strcasecmp(key, "--raw") == 0)
|
||||
{
|
||||
optionParseString(key, value, &instance->config.logoSource);
|
||||
instance->config.logoType = FF_LOGO_TYPE_RAW;
|
||||
}
|
||||
else if(strcasecmp(key, "--chafa") == 0)
|
||||
{
|
||||
optionParseString(key, value, &instance->config.logoSource);
|
||||
instance->config.logoType = FF_LOGO_TYPE_CHAFA;
|
||||
}
|
||||
|
||||
///////////////////
|
||||
//Display options//
|
||||
///////////////////
|
||||
|
||||
else if(strcasecmp(key, "--show-errors") == 0)
|
||||
instance->config.showErrors = optionParseBoolean(value);
|
||||
else if(strcasecmp(key, "--disable-linewrap") == 0)
|
||||
instance->config.disableLinewrap = optionParseBoolean(value);
|
||||
else if(strcasecmp(key, "--hide-cursor") == 0)
|
||||
instance->config.hideCursor = optionParseBoolean(value);
|
||||
else if(strcasecmp(key, "-s") == 0 || strcasecmp(key, "--structure") == 0)
|
||||
optionParseString(key, value, &data->structure);
|
||||
else if(strcasecmp(key, "--separator") == 0)
|
||||
optionParseString(key, value, &instance->config.separator);
|
||||
else if(strcasecmp(key, "-c") == 0 || strcasecmp(key, "--color") == 0)
|
||||
optionParseColor(key, value, &instance->config.mainColor);
|
||||
else if(strcasecmp(key, "--set") == 0)
|
||||
{
|
||||
if(value == NULL)
|
||||
@@ -710,43 +835,11 @@ static void parseOption(FFinstance* instance, FFdata* data, const char* key, con
|
||||
|
||||
ffValuestoreSet(&data->valuestore, value, separator + 1);
|
||||
}
|
||||
else if(strcasecmp(key, "-r") == 0 || strcasecmp(key, "--recache") == 0)
|
||||
{
|
||||
//Set cacheSave as well, beacuse the user expects the values to be cached when expliciting using --recache
|
||||
instance->config.recache = optionParseBoolean(value);
|
||||
instance->config.cacheSave = instance->config.recache;
|
||||
}
|
||||
else if(strcasecmp(key, "--nocache") == 0)
|
||||
{
|
||||
instance->config.recache = optionParseBoolean(value);
|
||||
instance->config.cacheSave = false;
|
||||
}
|
||||
else if(strcasecmp(key, "--load-config") == 0)
|
||||
optionParseConfigFile(instance, data, key, value);
|
||||
else if(strcasecmp(key, "--show-errors") == 0)
|
||||
instance->config.showErrors = optionParseBoolean(value);
|
||||
else if(strcasecmp(key, "--color-logo") == 0)
|
||||
instance->config.colorLogo = optionParseBoolean(value);
|
||||
else if(strcasecmp(key, "--print-remaining-logo") == 0)
|
||||
instance->config.printRemainingLogo = optionParseBoolean(value);
|
||||
else if(strcasecmp(key, "--multithreading") == 0)
|
||||
data->multithreading = optionParseBoolean(value);
|
||||
else if(strcasecmp(key, "--allow-slow-operations") == 0)
|
||||
instance->config.allowSlowOperations = optionParseBoolean(value);
|
||||
else if(strcasecmp(key, "--disable-linewrap") == 0)
|
||||
instance->config.disableLinewrap = optionParseBoolean(value);
|
||||
else if(strcasecmp(key, "--hide-cursor") == 0)
|
||||
instance->config.hideCursor = optionParseBoolean(value);
|
||||
else if(strcasecmp(key, "--logo-raw") == 0)
|
||||
instance->config.userLogoIsRaw = optionParseBoolean(value);
|
||||
else if(strcasecmp(key, "--structure") == 0)
|
||||
optionParseString(key, value, &data->structure);
|
||||
else if(strcasecmp(key, "-l") == 0 || strcasecmp(key, "--logo") == 0)
|
||||
optionParseString(key, value, &data->logoName);
|
||||
else if(strcasecmp(key, "-s") == 0 || strcasecmp(key, "--separator") == 0)
|
||||
optionParseString(key, value, &instance->config.separator);
|
||||
else if(strcasecmp(key, "-c") == 0 || strcasecmp(key, "--color") == 0)
|
||||
optionParseColor(key, value, &instance->config.color);
|
||||
|
||||
////////////////////////
|
||||
//Format + Key options//
|
||||
////////////////////////
|
||||
|
||||
else if(strcasecmp(key, "--os-format") == 0)
|
||||
optionParseString(key, value, &instance->config.osFormat);
|
||||
else if(strcasecmp(key, "--os-key") == 0)
|
||||
@@ -871,6 +964,11 @@ static void parseOption(FFinstance* instance, FFdata* data, const char* key, con
|
||||
optionParseString(key, value, &instance->config.timeKey);
|
||||
else if(strcasecmp(key, "--time-format") == 0)
|
||||
optionParseString(key, value, &instance->config.timeFormat);
|
||||
|
||||
///////////////////
|
||||
//Library options//
|
||||
///////////////////
|
||||
|
||||
else if(strcasecmp(key, "--lib-PCI") == 0)
|
||||
optionParseString(key, value, &instance->config.libPCI);
|
||||
else if(strcasecmp(key, "--lib-vulkan") == 0)
|
||||
@@ -893,8 +991,19 @@ static void parseOption(FFinstance* instance, FFdata* data, const char* key, con
|
||||
optionParseString(key, value, &instance->config.libDBus);
|
||||
else if(strcasecmp(key, "--lib-XFConf") == 0)
|
||||
optionParseString(key, value, &instance->config.libXFConf);
|
||||
else if(strcasecmp(key, "--lib-rpm") == 0)
|
||||
optionParseString(key, value, &instance->config.librpm);
|
||||
else if(strcasecmp(key, "--lib-sqlite") == 0 || strcasecmp(key, "--lib-sqlite3") == 0)
|
||||
optionParseString(key, value, &instance->config.libSQLite3);
|
||||
else if(strcasecmp(key, "--lib-imagemagick") == 0)
|
||||
optionParseString(key, value, &instance->config.libImageMagick);
|
||||
else if(strcasecmp(key, "--lib-z") == 0)
|
||||
optionParseString(key, value, &instance->config.libZ);
|
||||
else if(strcasecmp(key, "--lib-chafa") == 0)
|
||||
optionParseString(key, value, &instance->config.libChafa);
|
||||
|
||||
//////////////////
|
||||
//Module options//
|
||||
//////////////////
|
||||
|
||||
else if(strcasecmp(key, "--disk-folders") == 0)
|
||||
optionParseString(key, value, &instance->config.diskFolders);
|
||||
else if(strcasecmp(key, "--battery-dir") == 0)
|
||||
@@ -912,33 +1021,12 @@ static void parseOption(FFinstance* instance, FFdata* data, const char* key, con
|
||||
else if(strcasecmp(key, "--player-name") == 0)
|
||||
optionParseString(key, value, &instance->config.playerName);
|
||||
else if(strcasecmp(key, "--public-ip-timeout") == 0)
|
||||
{
|
||||
if(value == NULL)
|
||||
{
|
||||
fprintf(stderr, "Error: usage: %s <value>\n", key);
|
||||
exit(465);
|
||||
}
|
||||
instance->config.publicIpTimeout = optionParseUInt32(key, value);
|
||||
|
||||
if(sscanf(value, "%u", &instance->config.publicIpTimeout) != 1)
|
||||
{
|
||||
fprintf(stderr, "Error: couldn't parse %s to uint32_t\n", value);
|
||||
exit(466);
|
||||
}
|
||||
}
|
||||
else if(strncasecmp(key, "--color-", 7) == 0 && key[8] != '\0' && key[9] == '\0') // matches "--color-*"
|
||||
{
|
||||
//Map the number to an array index, so that '1' -> 0, '2' -> 1, etc.
|
||||
int index = (int)key[8] - 49;
|
||||
//////////////////
|
||||
//Unknown option//
|
||||
//////////////////
|
||||
|
||||
//Match only --color-[1-9]
|
||||
if(index < 0 || index >= FASTFETCH_LOGO_MAX_COLORS)
|
||||
{
|
||||
fprintf(stderr, "Error: invalid --color-[1-9] index: %c\n", key[8]);
|
||||
exit(472);
|
||||
}
|
||||
|
||||
optionParseColor(key, value, &data->logoColors[index]);
|
||||
}
|
||||
else
|
||||
{
|
||||
fprintf(stderr, "Error: unknown option: %s\n", key);
|
||||
@@ -1088,32 +1176,11 @@ int main(int argc, const char** argv)
|
||||
FFdata data;
|
||||
ffValuestoreInit(&data.valuestore);
|
||||
ffStrbufInitA(&data.structure, 256);
|
||||
ffStrbufInitA(&data.logoName, 0);
|
||||
data.multithreading = true;
|
||||
|
||||
for(uint8_t i = 0; i < FASTFETCH_LOGO_MAX_COLORS; i++)
|
||||
ffStrbufInitA(&data.logoColors[i], 0);
|
||||
|
||||
parseDefaultConfigFile(&instance, &data);
|
||||
parseArguments(&instance, &data, argc, argv);
|
||||
|
||||
//Load custom logo if it exists
|
||||
if(data.logoName.length > 0)
|
||||
ffLoadLogoSet(&instance, data.logoName.chars);
|
||||
else
|
||||
ffLoadLogo(&instance);
|
||||
|
||||
//If we haven't set key color, use primary color of logo
|
||||
if(instance.config.color.length == 0)
|
||||
ffStrbufSet(&instance.config.color, &instance.config.logoColors[0]);
|
||||
|
||||
//Overwrite logo colors with custom colors
|
||||
for(uint8_t i = 0; i < FASTFETCH_LOGO_MAX_COLORS; i++)
|
||||
{
|
||||
if(data.logoColors[i].length > 0)
|
||||
ffStrbufSet(&instance.config.logoColors[i], &data.logoColors[i]);
|
||||
}
|
||||
|
||||
//Start detection threads
|
||||
if(data.multithreading)
|
||||
ffStartDetectionThreads(&instance);
|
||||
|
||||
+86
-73
@@ -31,32 +31,37 @@ static inline void ffUnused(int dummy, ...) { (void) dummy; }
|
||||
|
||||
#define FASTFETCH_LOGO_MAX_COLORS 9 //two digits would make parsing much more complicated (index 1 - 9)
|
||||
|
||||
typedef struct FFlogo
|
||||
typedef enum FFLogoType
|
||||
{
|
||||
const char** names; //Null terminated
|
||||
char* lines;
|
||||
bool isFromUser;
|
||||
const char** builtinColors; // [0] is used as key color, if not user specified
|
||||
} FFlogo;
|
||||
FF_LOGO_TYPE_AUTO, //If something is given, first try builtin, then file. Otherwise detect logo
|
||||
FF_LOGO_TYPE_BUILTIN, //Builtin ascii art.
|
||||
FF_LOGO_TYPE_FILE, //Raw text file, printed as is.
|
||||
FF_LOGO_TYPE_RAW, //Raw text file, printed with color codes replacement.
|
||||
FF_LOGO_TYPE_SIXEL, //Image file, printed as sixel codes.
|
||||
FF_LOGO_TYPE_KITTY, //Image file, printed as kitty graphics protocol
|
||||
FF_LOGO_TYPE_CHAFA //Image file, printed as ascii art using libchafa
|
||||
} FFLogoType;
|
||||
|
||||
typedef struct FFconfig
|
||||
{
|
||||
const FFlogo* logo;
|
||||
FFstrbuf logoSource;
|
||||
FFLogoType logoType;
|
||||
FFstrbuf logoColors[FASTFETCH_LOGO_MAX_COLORS];
|
||||
uint32_t logoWidth;
|
||||
uint32_t logoHeight;
|
||||
uint32_t logoPaddingLeft;
|
||||
uint32_t logoPaddingRight;
|
||||
bool logoPrintRemaining;
|
||||
|
||||
uint16_t logoKeySpacing;
|
||||
FFstrbuf mainColor; //If this is empty, ffPrintLogo will set it to the main color of the logo
|
||||
FFstrbuf separator;
|
||||
int16_t offsetx;
|
||||
FFstrbuf color;
|
||||
bool colorLogo;
|
||||
|
||||
bool showErrors;
|
||||
bool recache;
|
||||
bool cacheSave;
|
||||
bool printRemainingLogo;
|
||||
bool allowSlowOperations;
|
||||
bool disableLinewrap;
|
||||
bool hideCursor;
|
||||
bool userLogoIsRaw;
|
||||
|
||||
FFstrbuf osFormat;
|
||||
FFstrbuf osKey;
|
||||
@@ -132,7 +137,10 @@ typedef struct FFconfig
|
||||
FFstrbuf libDConf;
|
||||
FFstrbuf libDBus;
|
||||
FFstrbuf libXFConf;
|
||||
FFstrbuf librpm;
|
||||
FFstrbuf libSQLite3;
|
||||
FFstrbuf libImageMagick;
|
||||
FFstrbuf libZ;
|
||||
FFstrbuf libChafa;
|
||||
|
||||
FFstrbuf diskFolders;
|
||||
|
||||
@@ -154,7 +162,8 @@ typedef struct FFconfig
|
||||
typedef struct FFstate
|
||||
{
|
||||
uint32_t logoWidth;
|
||||
char* logoLinesIndex;
|
||||
uint32_t logoHeight;
|
||||
uint32_t keysHeight;
|
||||
|
||||
struct passwd* passwd;
|
||||
struct utsname utsname;
|
||||
@@ -259,7 +268,6 @@ typedef struct FFMediaResult
|
||||
{
|
||||
FFstrbuf busNameShort; //e.g. plasma-browser-integration
|
||||
FFstrbuf player; // e.g. Google Chrome
|
||||
FFstrbuf playerPretty; // e.g. YouTube (Google Chrome)
|
||||
FFstrbuf song;
|
||||
FFstrbuf artist;
|
||||
FFstrbuf album;
|
||||
@@ -376,9 +384,9 @@ typedef enum FFInitState
|
||||
#define FF_LIBRARY_LOAD_SYMBOL(library, symbolName, returnValue) \
|
||||
__typeof__(&symbolName) FF_LIBRARY_LOAD_SYMBOL_ADRESS(library, ff ## symbolName, symbolName, returnValue);
|
||||
|
||||
/*************************/
|
||||
/* Common util functions */
|
||||
/*************************/
|
||||
//////////////////////
|
||||
// Common functions //
|
||||
//////////////////////
|
||||
|
||||
//common/init.c
|
||||
void ffInitInstance(FFinstance* instance);
|
||||
@@ -391,37 +399,36 @@ void ffListFeatures();
|
||||
void ffStartDetectionThreads(FFinstance* instance);
|
||||
|
||||
//common/io.c
|
||||
void ffPrintLogoAndKey(FFinstance* instance, const char* moduleName, uint8_t moduleIndex, const FFstrbuf* customKeyFormat);
|
||||
void ffPrintError(FFinstance* instance, const char* moduleName, uint8_t moduleIndex, const FFstrbuf* customKeyFormat, const FFstrbuf* formatString, uint32_t numFormatArgs, const char* message, ...);
|
||||
void ffPrintFormatString(FFinstance* instance, const char* moduleName, uint8_t moduleIndex, const FFstrbuf* customKeyFormat, const FFstrbuf* formatString, const FFstrbuf* error, uint32_t numArgs, const FFformatarg* arguments);
|
||||
void ffGetCacheFilePath(FFinstance* instance, const char* moduleName, const char* extension, FFstrbuf* buffer);
|
||||
void ffReadCacheFile(FFinstance* instance, const char* moduleName, const char* extension, FFstrbuf* buffer);
|
||||
void ffWriteCacheFile(FFinstance* instance, const char* moduleName, const char* extension, FFstrbuf* content);
|
||||
bool ffPrintFromCache(FFinstance* instance, const char* moduleName, const FFstrbuf* customKeyFormat, const FFstrbuf* formatString, uint32_t numArgs);
|
||||
void ffPrintAndSaveToCache(FFinstance* instance, const char* moduleName, const FFstrbuf* customKeyFormat, const FFstrbuf* value, const FFstrbuf* formatString, uint32_t numArgs, const FFformatarg* arguments);
|
||||
void ffPrintAndAppendToCache(FFinstance* instance, const char* moduleName, uint8_t moduleIndex, const FFstrbuf* customKeyFormat, FFcache* cache, const FFstrbuf* value, const FFstrbuf* formatString, uint32_t numArgs, const FFformatarg* arguments);
|
||||
|
||||
void ffCacheValidate(FFinstance* instance);
|
||||
void ffCacheOpenWrite(FFinstance* instance, const char* moduleName, FFcache* cache);
|
||||
void ffCacheClose(FFcache* cache);
|
||||
|
||||
void ffAppendFDContent(int fd, FFstrbuf* buffer);
|
||||
bool ffAppendFileContent(const char* fileName, FFstrbuf* buffer); //returns true if open() succeeds. This is used to differentiate between <file not found> and <empty file>
|
||||
bool ffGetFileContent(const char* fileName, FFstrbuf* buffer);
|
||||
bool ffWriteFDContent(int fd, const FFstrbuf* content);
|
||||
void ffWriteFileContent(const char* fileName, const FFstrbuf* buffer);
|
||||
bool ffWriteFileContent(const char* fileName, const FFstrbuf* buffer);
|
||||
|
||||
bool ffFileExists(const char* fileName, mode_t mode);
|
||||
void ffSuppressIO(bool suppress); // Not thread safe!
|
||||
|
||||
// Not thread safe!
|
||||
void ffSuppressIO(bool suppress);
|
||||
void ffGetTerminalResponse(const char* request, const char* format, ...);
|
||||
|
||||
//common/printing.c
|
||||
void ffPrintError(FFinstance* instance, const char* moduleName, uint8_t moduleIndex, const FFstrbuf* customKeyFormat, const FFstrbuf* formatString, uint32_t numFormatArgs, const char* message, ...);
|
||||
void ffPrintFormatString(FFinstance* instance, const char* moduleName, uint8_t moduleIndex, const FFstrbuf* customKeyFormat, const FFstrbuf* formatString, const FFstrbuf* error, uint32_t numArgs, const FFformatarg* arguments);
|
||||
void ffPrintColor(const FFstrbuf* colorValue);
|
||||
void ffPrintCharTimes(char c, uint32_t times);
|
||||
|
||||
// They return true if the file was found, independently if start was found
|
||||
// Buffers which already contain content are not overwritten
|
||||
// The last occurence of start in the first file will be the one used
|
||||
// The *Values methods always return true, if all properties were already found before, without testing if the file exists
|
||||
//common/caching.c
|
||||
void ffCacheValidate(FFinstance* instance);
|
||||
|
||||
void ffCacheOpenWrite(FFinstance* instance, const char* moduleName, FFcache* cache);
|
||||
void ffCacheClose(FFcache* cache);
|
||||
|
||||
bool ffPrintFromCache(FFinstance* instance, const char* moduleName, const FFstrbuf* customKeyFormat, const FFstrbuf* formatString, uint32_t numArgs);
|
||||
void ffPrintAndAppendToCache(FFinstance* instance, const char* moduleName, uint8_t moduleIndex, const FFstrbuf* customKeyFormat, FFcache* cache, const FFstrbuf* value, const FFstrbuf* formatString, uint32_t numArgs, const FFformatarg* arguments);
|
||||
void ffPrintAndWriteToCache(FFinstance* instance, const char* moduleName, const FFstrbuf* customKeyFormat, const FFstrbuf* value, const FFstrbuf* formatString, uint32_t numArgs, const FFformatarg* arguments);
|
||||
|
||||
//common/properties.c
|
||||
bool ffParsePropLine(const char* line, const char* start, FFstrbuf* buffer);
|
||||
bool ffParsePropLines(const char* lines, const char* start, FFstrbuf* buffer);
|
||||
bool ffParsePropFileValues(const char* filename, uint32_t numQueries, FFpropquery* queries);
|
||||
bool ffParsePropFile(const char* filename, const char* start, FFstrbuf* buffer);
|
||||
bool ffParsePropFileHomeValues(const FFinstance* instance, const char* relativeFile, uint32_t numQueries, FFpropquery* queries);
|
||||
@@ -429,6 +436,21 @@ bool ffParsePropFileHome(const FFinstance* instance, const char* relativeFile, c
|
||||
bool ffParsePropFileConfigValues(const FFinstance* instance, const char* relativeFile, uint32_t numQueries, FFpropquery* queries);
|
||||
bool ffParsePropFileConfig(const FFinstance* instance, const char* relativeFile, const char* start, FFstrbuf* buffer);
|
||||
|
||||
//common/font.c
|
||||
void ffFontInitQt(FFfont* font, const char* data);
|
||||
void ffFontInitPango(FFfont* font, const char* data);
|
||||
void ffFontInitCopy(FFfont* font, const char* name);
|
||||
void ffFontDestroy(FFfont* font);
|
||||
|
||||
//common/format.c
|
||||
void ffFormatAppendFormatArg(FFstrbuf* buffer, const FFformatarg* formatarg);
|
||||
void ffParseFormatString(FFstrbuf* buffer, const FFstrbuf* formatstr, const FFstrbuf* error, uint32_t numArgs, const FFformatarg* arguments);
|
||||
|
||||
//common/parsing.c
|
||||
bool ffStrSet(const char* str);
|
||||
void ffParseSemver(FFstrbuf* buffer, const FFstrbuf* major, const FFstrbuf* minor, const FFstrbuf* patch);
|
||||
void ffParseGTK(FFstrbuf* buffer, const FFstrbuf* gtk2, const FFstrbuf* gtk3, const FFstrbuf* gtk4);
|
||||
|
||||
//common/processing.c
|
||||
void ffProcessAppendStdOut(FFstrbuf* buffer, char* const argv[]);
|
||||
|
||||
@@ -438,47 +460,38 @@ void* ffLibraryLoad(const FFstrbuf* userProvidedName, ...);
|
||||
//common/networking.c
|
||||
void ffNetworkingGetHttp(const char* host, const char* path, uint32_t timeout, FFstrbuf* buffer);
|
||||
|
||||
//common/logo.c
|
||||
void ffLoadLogoSet(FFinstance* instance, const char* logo);
|
||||
void ffLoadLogo(FFinstance* instance);
|
||||
void ffPrintLogoLine(FFinstance* instance);
|
||||
void ffPrintRemainingLogo(FFinstance* instance);
|
||||
|
||||
void ffPrintLogos(FFinstance* instance);
|
||||
void ffListLogos();
|
||||
void ffListLogosForAutocompletion();
|
||||
|
||||
//common/format.c
|
||||
void ffFormatAppendFormatArg(FFstrbuf* buffer, const FFformatarg* formatarg);
|
||||
void ffParseFormatString(FFstrbuf* buffer, const FFstrbuf* formatstr, const FFstrbuf* error, uint32_t numArgs, const FFformatarg* arguments);
|
||||
|
||||
//common/parsing.c
|
||||
void ffGetGtkPretty(FFstrbuf* buffer, const FFstrbuf* gtk2, const FFstrbuf* gtk3, const FFstrbuf* gtk4);
|
||||
|
||||
void ffFontInitQt(FFfont* font, const char* data);
|
||||
void ffFontInitPango(FFfont* font, const char* data);
|
||||
void ffFontInitCopy(FFfont* font, const char* name);
|
||||
void ffFontDestroy(FFfont* font);
|
||||
|
||||
bool ffGetPropValue(const char* line, const char* start, FFstrbuf* buffer);
|
||||
bool ffGetPropValueFromLines(const char* lines, const char* start, FFstrbuf* buffer);
|
||||
|
||||
void ffParseSemver(FFstrbuf* buffer, const FFstrbuf* major, const FFstrbuf* minor, const FFstrbuf* patch);
|
||||
|
||||
bool ffStrSet(const char* str);
|
||||
|
||||
//common/settings.c
|
||||
FFvariant ffSettingsGetDConf(FFinstance* instance, const char* key, FFvarianttype type);
|
||||
FFvariant ffSettingsGetGSettings(FFinstance* instance, const char* schemaName, const char* path, const char* key, FFvarianttype type);
|
||||
FFvariant ffSettingsGet(FFinstance* instance, const char* dconfKey, const char* gsettingsSchemaName, const char* gsettingsPath, const char* gsettingsKey, FFvarianttype type);
|
||||
FFvariant ffSettingsGetXFConf(FFinstance* instance, const char* channelName, const char* propertyName, FFvarianttype type);
|
||||
|
||||
int ffSettingsGetSQLite3Int(FFinstance* instance, const char* dbPath, const char* query);
|
||||
|
||||
#ifdef __ANDROID__
|
||||
bool ffSettingsGetAndroidProperty(const char* propName, FFstrbuf* result);
|
||||
#endif
|
||||
|
||||
////////////////////
|
||||
// Logo functions //
|
||||
////////////////////
|
||||
|
||||
void ffPrintLogo(FFinstance* instance);
|
||||
void ffPrintRemainingLogo(FFinstance* instance);
|
||||
|
||||
void ffPrintLogoLine(FFinstance* instance);
|
||||
void ffPrintLogoAndKey(FFinstance* instance, const char* moduleName, uint8_t moduleIndex, const FFstrbuf* customKeyFormat);
|
||||
|
||||
void ffPrintBuiltinLogos(FFinstance* instance);
|
||||
void ffListBuiltinLogos();
|
||||
void ffListBuiltinLogosAutocompletion();
|
||||
|
||||
/////////////////////////
|
||||
// Detection functions //
|
||||
/////////////////////////
|
||||
|
||||
//detection/os.c
|
||||
const FFOSResult* ffDetectOS(FFinstance* instance);
|
||||
const FFOSResult* ffDetectOS(const FFinstance* instance);
|
||||
|
||||
//detection/plasma.c
|
||||
const FFPlasmaResult* ffDetectPlasma(FFinstance* instance);
|
||||
@@ -503,9 +516,9 @@ const FFMediaResult* ffDetectMedia(FFinstance* instance);
|
||||
//detection/dateTime.c
|
||||
const FFDateTimeResult* ffDetectDateTime(const FFinstance* instance);
|
||||
|
||||
/********************/
|
||||
/* Module functions */
|
||||
/********************/
|
||||
//////////////////////
|
||||
// Module functions //
|
||||
//////////////////////
|
||||
|
||||
//Common
|
||||
const FFTitleResult* ffDetectTitle(FFinstance* instance);
|
||||
|
||||
@@ -4,7 +4,11 @@
|
||||
#define FASTFETCH_PROJECT_NAME "@PROJECT_NAME@"
|
||||
#define FASTFETCH_PROJECT_VERSION "@PROJECT_VERSION@"
|
||||
#define FASTFETCH_PROJECT_VERSION_MAJOR @PROJECT_VERSION_MAJOR@
|
||||
#define FASTFETCH_PROJECT_VERSION_EXTRA "@PROJECT_VERSION_EXTRA@"
|
||||
#define FASTFETCH_PROJECT_VERSION_TWEAK "@PROJECT_VERSION_TWEAK@"
|
||||
|
||||
#define FASTFETCH_TARGET_DIR_ROOT "@TARGET_DIR_ROOT@"
|
||||
#define FASTFETCH_TARGET_DIR_USR "@TARGET_DIR_USR@"
|
||||
#define FASTFETCH_TARGET_DIR_HOME "@TARGET_DIR_HOME@"
|
||||
|
||||
#define FASTFETCH_DATATEXT_STRUCTURE "@DATATEXT_STRUCTURE@"
|
||||
#define FASTFETCH_DATATEXT_CONFIG "@DATATEXT_CONFIG@" //Requires FASTFETCH_PROJECT_VERSION and FASTFETCH_DATATEXT_STRUCTURE to be set
|
||||
|
||||
@@ -10,9 +10,6 @@ int main(int argc, char** argv)
|
||||
|
||||
//Modify instance.config here
|
||||
|
||||
ffLoadLogo(&instance); //ffLoadLogoSet(&instance, "my custom logo");
|
||||
ffStrbufSet(&instance.config.color, &instance.config.logoColors[0]); //Use the primary color of the logo as key color
|
||||
|
||||
//Multithreading --> better performance
|
||||
ffStartDetectionThreads(&instance);
|
||||
|
||||
|
||||
+1660
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,29 @@
|
||||
#ifdef FF_HAVE_IMAGEMAGICK6
|
||||
|
||||
#include "image.h"
|
||||
#include <magick/MagickCore.h>
|
||||
|
||||
static FF_LIBRARY_SYMBOL(ResizeImage);
|
||||
|
||||
static void* logoResize(const void* image, size_t width, size_t height, void* exceptionInfo)
|
||||
{
|
||||
return ffResizeImage(image, width, height, UndefinedFilter, 1.0, exceptionInfo);
|
||||
}
|
||||
|
||||
FFLogoImageResult ffLogoPrintImageIM6(FFinstance* instance, FFLogoRequestData* requestData)
|
||||
{
|
||||
FF_LIBRARY_LOAD(imageMagick, instance->config.libImageMagick, FF_LOGO_IMAGE_RESULT_INIT_ERROR, "libMagickCore-6.Q16HDRI.so", 8, "libMagickCore-6.Q16.so", 8)
|
||||
FF_LIBRARY_LOAD_SYMBOL_ADRESS(imageMagick, ffResizeImage, ResizeImage, FF_LOGO_IMAGE_RESULT_INIT_ERROR);
|
||||
|
||||
FFIMData imData;
|
||||
imData.resizeFunc = logoResize;
|
||||
imData.library = imageMagick;
|
||||
|
||||
FFLogoImageResult result = ffLogoPrintImageImpl(instance, requestData, &imData);
|
||||
|
||||
dlclose(imageMagick);
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,29 @@
|
||||
#ifdef FF_HAVE_IMAGEMAGICK7
|
||||
|
||||
#include "image.h"
|
||||
#include <MagickCore/MagickCore.h>
|
||||
|
||||
static FF_LIBRARY_SYMBOL(ResizeImage);
|
||||
|
||||
static void* logoResize(const void* image, size_t width, size_t height, void* exceptionInfo)
|
||||
{
|
||||
return ffResizeImage(image, width, height, UndefinedFilter, exceptionInfo);
|
||||
}
|
||||
|
||||
FFLogoImageResult ffLogoPrintImageIM7(FFinstance* instance, FFLogoRequestData* requestData)
|
||||
{
|
||||
FF_LIBRARY_LOAD(imageMagick, instance->config.libImageMagick, FF_LOGO_IMAGE_RESULT_INIT_ERROR, "libMagickCore-7.Q16HDRI.so", 11, "libMagickCore-7.Q16.so", 11)
|
||||
FF_LIBRARY_LOAD_SYMBOL_ADRESS(imageMagick, ffResizeImage, ResizeImage, FF_LOGO_IMAGE_RESULT_INIT_ERROR);
|
||||
|
||||
FFIMData imData;
|
||||
imData.resizeFunc = logoResize;
|
||||
imData.library = imageMagick;
|
||||
|
||||
FFLogoImageResult result = ffLogoPrintImageImpl(instance, requestData, &imData);
|
||||
|
||||
dlclose(imageMagick);
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,571 @@
|
||||
#include "image.h"
|
||||
|
||||
#if defined(FF_HAVE_IMAGEMAGICK7) || defined(FF_HAVE_IMAGEMAGICK6)
|
||||
|
||||
#define FF_KITTY_MAX_CHUNK_SIZE 4096
|
||||
|
||||
#define FF_CACHE_FILE_HEIGHT "height"
|
||||
#define FF_CACHE_FILE_WIDTH "width"
|
||||
#define FF_CACHE_FILE_SIXEL "sixel"
|
||||
#define FF_CACHE_FILE_KITTY_COMPRESSED "kittyc"
|
||||
#define FF_CACHE_FILE_KITTY_UNCOMPRESSED "kittyu"
|
||||
#define FF_CACHE_FILE_CHAFA "chafa"
|
||||
|
||||
#include <string.h>
|
||||
#include <unistd.h>
|
||||
#include <fcntl.h>
|
||||
#include <sys/ioctl.h>
|
||||
|
||||
#ifdef FF_HAVE_ZLIB
|
||||
#include <zlib.h>
|
||||
|
||||
static bool compressBlob(const FFinstance* instance, void** blob, size_t* length)
|
||||
{
|
||||
FF_LIBRARY_LOAD(zlib, instance->config.libZ, false, "libz.so", 2)
|
||||
FF_LIBRARY_LOAD_SYMBOL(zlib, compressBound, false)
|
||||
FF_LIBRARY_LOAD_SYMBOL(zlib, compress2, false)
|
||||
|
||||
uLong compressedLength = ffcompressBound(*length);
|
||||
void* compressed = malloc(compressedLength);
|
||||
if(compressed == NULL)
|
||||
{
|
||||
dlclose(zlib);
|
||||
return false;
|
||||
}
|
||||
|
||||
int compressResult = ffcompress2(compressed, &compressedLength, *blob, *length, Z_BEST_COMPRESSION);
|
||||
dlclose(zlib);
|
||||
if(compressResult != Z_OK)
|
||||
{
|
||||
free(compressed);
|
||||
return false;
|
||||
}
|
||||
|
||||
free(*blob);
|
||||
|
||||
*length = (size_t) compressedLength;
|
||||
*blob = compressed;
|
||||
return true;
|
||||
}
|
||||
|
||||
#endif // FF_HAVE_ZLIB
|
||||
|
||||
//We use only the defines from here, that are exactly the same in both versions
|
||||
#ifdef FF_HAVE_IMAGEMAGICK7
|
||||
#include <MagickCore/MagickCore.h>
|
||||
#else
|
||||
#include <magick/MagickCore.h>
|
||||
#endif
|
||||
|
||||
typedef struct ImageData
|
||||
{
|
||||
FF_LIBRARY_SYMBOL(CopyMagickString)
|
||||
FF_LIBRARY_SYMBOL(ImageToBlob)
|
||||
FF_LIBRARY_SYMBOL(Base64Encode)
|
||||
|
||||
ImageInfo* imageInfo;
|
||||
Image* image;
|
||||
ExceptionInfo* exceptionInfo;
|
||||
} ImageData;
|
||||
|
||||
static inline bool checkAllocationResult(void* data, size_t length)
|
||||
{
|
||||
if(data == NULL)
|
||||
return false;
|
||||
|
||||
if(length == 0)
|
||||
{
|
||||
free(data);
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
static inline uint32_t simpleCeil(double value)
|
||||
{
|
||||
uint32_t result = (uint32_t) value;
|
||||
return value == (double) result ? result : result + 1;
|
||||
}
|
||||
|
||||
static void writeCacheStrbuf(FFLogoRequestData* requestData, const FFstrbuf* value, const char* cacheFileName)
|
||||
{
|
||||
uint32_t cacheDirLength = requestData->cacheDir.length;
|
||||
ffStrbufAppendS(&requestData->cacheDir, cacheFileName);
|
||||
ffWriteFileContent(requestData->cacheDir.chars, value);
|
||||
ffStrbufSubstrBefore(&requestData->cacheDir, cacheDirLength);
|
||||
}
|
||||
|
||||
static void writeCacheUint32(FFLogoRequestData* requestData, uint32_t value, const char* cacheFileName)
|
||||
{
|
||||
FFstrbuf content;
|
||||
content.chars = (char*) &value;
|
||||
content.length = sizeof(value);
|
||||
writeCacheStrbuf(requestData, &content, cacheFileName);
|
||||
}
|
||||
|
||||
static void printImagePixels(FFinstance* instance, FFLogoRequestData* requestData, const FFstrbuf* result, const char* cacheFileName)
|
||||
{
|
||||
//Calculate character dimensions
|
||||
instance->state.logoWidth = requestData->logoCharacterWidth + instance->config.logoPaddingLeft + instance->config.logoPaddingRight;
|
||||
|
||||
instance->state.logoHeight = requestData->logoCharacterHeight;
|
||||
if(requestData->type == FF_LOGO_TYPE_KITTY)
|
||||
instance->state.logoHeight -= 1;
|
||||
|
||||
//Write cache files
|
||||
writeCacheStrbuf(requestData, result, cacheFileName);
|
||||
|
||||
if(instance->config.logoWidth == 0)
|
||||
writeCacheUint32(requestData, instance->state.logoWidth, FF_CACHE_FILE_WIDTH);
|
||||
|
||||
if(instance->config.logoHeight == 0)
|
||||
writeCacheUint32(requestData, instance->state.logoHeight, FF_CACHE_FILE_HEIGHT);
|
||||
|
||||
//Write result to stdout
|
||||
ffPrintCharTimes(' ', instance->config.logoPaddingLeft);
|
||||
fflush(stdout);
|
||||
ffWriteFDContent(STDOUT_FILENO, result);
|
||||
|
||||
//Go to upper left corner
|
||||
fputs("\033[9999999D", stdout);
|
||||
printf("\033[%uA", instance->state.logoHeight);
|
||||
}
|
||||
|
||||
static bool printImageSixel(FFinstance* instance, FFLogoRequestData* requestData, const ImageData* imageData)
|
||||
{
|
||||
imageData->ffCopyMagickString(imageData->imageInfo->magick, "SIXEL", 6);
|
||||
|
||||
size_t length;
|
||||
void* blob = imageData->ffImageToBlob(imageData->imageInfo, imageData->image, &length, imageData->exceptionInfo);
|
||||
if(!checkAllocationResult(blob, length))
|
||||
return false;
|
||||
|
||||
FFstrbuf result;
|
||||
result.chars = (char*) blob;
|
||||
result.length = (uint32_t) length;
|
||||
|
||||
printImagePixels(instance, requestData, &result, FF_CACHE_FILE_SIXEL);
|
||||
|
||||
free(blob);
|
||||
return true;
|
||||
}
|
||||
|
||||
static void appendKittyChunk(FFstrbuf* result, const char** blob, size_t* length, bool printEscapeCode)
|
||||
{
|
||||
uint32_t chunkSize = *length > FF_KITTY_MAX_CHUNK_SIZE ? FF_KITTY_MAX_CHUNK_SIZE : (uint32_t) *length;
|
||||
|
||||
if(printEscapeCode)
|
||||
ffStrbufAppendS(result, "\033_G");
|
||||
else
|
||||
ffStrbufAppendC(result, ',');
|
||||
|
||||
ffStrbufAppendS(result, chunkSize != *length ? "m=1" : "m=0");
|
||||
ffStrbufAppendC(result, ';');
|
||||
ffStrbufAppendNS(result, chunkSize, *blob);
|
||||
ffStrbufAppendS(result, "\033\\");
|
||||
*length -= chunkSize;
|
||||
*blob += chunkSize;
|
||||
}
|
||||
|
||||
static bool printImageKitty(FFinstance* instance, FFLogoRequestData* requestData, const ImageData* imageData)
|
||||
{
|
||||
imageData->ffCopyMagickString(imageData->imageInfo->magick, "RGBA", 5);
|
||||
|
||||
size_t length;
|
||||
void* blob = imageData->ffImageToBlob(imageData->imageInfo, imageData->image, &length, imageData->exceptionInfo);
|
||||
if(!checkAllocationResult(blob, length))
|
||||
return false;
|
||||
|
||||
#ifdef FF_HAVE_ZLIB
|
||||
bool isCompressed = compressBlob(instance, &blob, &length);
|
||||
#else
|
||||
bool isCompressed = false;
|
||||
#endif
|
||||
|
||||
char* chars = imageData->ffBase64Encode(blob, length, &length);
|
||||
free(blob);
|
||||
if(!checkAllocationResult(chars, length))
|
||||
return false;
|
||||
|
||||
FFstrbuf result;
|
||||
ffStrbufInitA(&result, (uint32_t) (length + 1024));
|
||||
|
||||
const char* currentPos = chars;
|
||||
size_t remainingLength = length;
|
||||
|
||||
ffStrbufAppendF(&result, "\033_Ga=T,f=32,s=%u,v=%u", requestData->logoPixelWidth, requestData->logoPixelHeight);
|
||||
if(isCompressed)
|
||||
ffStrbufAppendS(&result, ",o=z");
|
||||
appendKittyChunk(&result, ¤tPos, &remainingLength, false);
|
||||
while(remainingLength > 0)
|
||||
appendKittyChunk(&result, ¤tPos, &remainingLength, true);
|
||||
|
||||
printImagePixels(instance, requestData, &result, isCompressed ? FF_CACHE_FILE_KITTY_COMPRESSED : FF_CACHE_FILE_KITTY_UNCOMPRESSED);
|
||||
|
||||
ffStrbufDestroy(&result);
|
||||
free(chars);
|
||||
return true;
|
||||
}
|
||||
|
||||
#ifdef FF_HAVE_CHAFA
|
||||
#include <chafa/chafa.h>
|
||||
static bool printImageChafa(FFinstance* instance, FFLogoRequestData* requestData, const ImageData* imageData)
|
||||
{
|
||||
FF_LIBRARY_LOAD(chafa, instance->config.libChafa, false, "libchafa.so", 1)
|
||||
FF_LIBRARY_LOAD_SYMBOL(chafa, chafa_symbol_map_new, false)
|
||||
FF_LIBRARY_LOAD_SYMBOL(chafa, chafa_symbol_map_add_by_tags, false)
|
||||
FF_LIBRARY_LOAD_SYMBOL(chafa, chafa_canvas_config_new, false)
|
||||
FF_LIBRARY_LOAD_SYMBOL(chafa, chafa_canvas_config_set_geometry, false)
|
||||
FF_LIBRARY_LOAD_SYMBOL(chafa, chafa_canvas_config_set_symbol_map, false)
|
||||
FF_LIBRARY_LOAD_SYMBOL(chafa, chafa_canvas_new, false)
|
||||
FF_LIBRARY_LOAD_SYMBOL(chafa, chafa_canvas_draw_all_pixels, false)
|
||||
FF_LIBRARY_LOAD_SYMBOL(chafa, chafa_canvas_print, false)
|
||||
FF_LIBRARY_LOAD_SYMBOL(chafa, chafa_canvas_unref, false)
|
||||
FF_LIBRARY_LOAD_SYMBOL(chafa, chafa_canvas_config_unref, false)
|
||||
FF_LIBRARY_LOAD_SYMBOL(chafa, chafa_symbol_map_unref, false)
|
||||
FF_LIBRARY_LOAD_SYMBOL(chafa, g_string_free, false)
|
||||
|
||||
imageData->ffCopyMagickString(imageData->imageInfo->magick, "RGBA", 5);
|
||||
size_t length;
|
||||
void* blob = imageData->ffImageToBlob(imageData->imageInfo, imageData->image, &length, imageData->exceptionInfo);
|
||||
if(!checkAllocationResult(blob, length))
|
||||
return false;
|
||||
|
||||
ChafaSymbolMap* symbolMap = ffchafa_symbol_map_new();
|
||||
ffchafa_symbol_map_add_by_tags(symbolMap, CHAFA_SYMBOL_TAG_ALL);
|
||||
|
||||
ChafaCanvasConfig* canvasConfig = ffchafa_canvas_config_new();
|
||||
ffchafa_canvas_config_set_geometry(canvasConfig, (gint) requestData->logoCharacterWidth, (gint) requestData->logoCharacterHeight);
|
||||
ffchafa_canvas_config_set_symbol_map(canvasConfig, symbolMap);
|
||||
|
||||
ChafaCanvas* canvas = ffchafa_canvas_new(canvasConfig);
|
||||
ffchafa_canvas_draw_all_pixels(
|
||||
canvas,
|
||||
CHAFA_PIXEL_RGBA8_UNASSOCIATED,
|
||||
blob,
|
||||
(gint) imageData->image->columns,
|
||||
(gint) imageData->image->rows,
|
||||
(gint) imageData->image->columns * 4
|
||||
);
|
||||
|
||||
GString* str = ffchafa_canvas_print(canvas, NULL);
|
||||
FFstrbuf result;
|
||||
result.allocated = (uint32_t) str->allocated_len;
|
||||
result.length = (uint32_t) str->len;
|
||||
result.chars = str->str;
|
||||
|
||||
ffLogoPrint(instance, result.chars, false);
|
||||
writeCacheStrbuf(requestData, &result, FF_CACHE_FILE_CHAFA);
|
||||
|
||||
ffg_string_free(str, TRUE);
|
||||
ffchafa_canvas_unref(canvas);
|
||||
ffchafa_canvas_config_unref(canvasConfig);
|
||||
ffchafa_symbol_map_unref(symbolMap);
|
||||
|
||||
return true;
|
||||
}
|
||||
#endif
|
||||
|
||||
FFLogoImageResult ffLogoPrintImageImpl(FFinstance* instance, FFLogoRequestData* requestData, const FFIMData* imData)
|
||||
{
|
||||
FF_LIBRARY_LOAD_SYMBOL(imData->library, AcquireExceptionInfo, FF_LOGO_IMAGE_RESULT_INIT_ERROR)
|
||||
FF_LIBRARY_LOAD_SYMBOL(imData->library, DestroyExceptionInfo, FF_LOGO_IMAGE_RESULT_INIT_ERROR)
|
||||
FF_LIBRARY_LOAD_SYMBOL(imData->library, AcquireImageInfo, FF_LOGO_IMAGE_RESULT_INIT_ERROR)
|
||||
FF_LIBRARY_LOAD_SYMBOL(imData->library, DestroyImageInfo, FF_LOGO_IMAGE_RESULT_INIT_ERROR)
|
||||
FF_LIBRARY_LOAD_SYMBOL(imData->library, ReadImage, FF_LOGO_IMAGE_RESULT_INIT_ERROR)
|
||||
FF_LIBRARY_LOAD_SYMBOL(imData->library, DestroyImage, FF_LOGO_IMAGE_RESULT_INIT_ERROR)
|
||||
|
||||
ImageData imageData;
|
||||
|
||||
FF_LIBRARY_LOAD_SYMBOL_ADRESS(imData->library, imageData.ffCopyMagickString, CopyMagickString, FF_LOGO_IMAGE_RESULT_INIT_ERROR)
|
||||
FF_LIBRARY_LOAD_SYMBOL_ADRESS(imData->library, imageData.ffImageToBlob, ImageToBlob, FF_LOGO_IMAGE_RESULT_INIT_ERROR)
|
||||
FF_LIBRARY_LOAD_SYMBOL_ADRESS(imData->library, imageData.ffBase64Encode, Base64Encode, FF_LOGO_IMAGE_RESULT_INIT_ERROR)
|
||||
|
||||
imageData.exceptionInfo = ffAcquireExceptionInfo();
|
||||
if(imageData.exceptionInfo == NULL)
|
||||
return FF_LOGO_IMAGE_RESULT_RUN_ERROR;
|
||||
|
||||
ImageInfo* imageInfoIn = ffAcquireImageInfo();
|
||||
if(imageInfoIn == NULL)
|
||||
{
|
||||
ffDestroyExceptionInfo(imageData.exceptionInfo);
|
||||
return FF_LOGO_IMAGE_RESULT_RUN_ERROR;
|
||||
}
|
||||
|
||||
//+1, because we need to copy the null byte too
|
||||
imageData.ffCopyMagickString(imageInfoIn->filename, instance->config.logoSource.chars, instance->config.logoSource.length + 1);
|
||||
|
||||
imageData.image = ffReadImage(imageInfoIn, imageData.exceptionInfo);
|
||||
ffDestroyImageInfo(imageInfoIn);
|
||||
if(imageData.image == NULL)
|
||||
{
|
||||
ffDestroyExceptionInfo(imageData.exceptionInfo);
|
||||
return FF_LOGO_IMAGE_RESULT_RUN_ERROR;
|
||||
}
|
||||
|
||||
if(requestData->logoPixelWidth == 0 && requestData->logoPixelHeight == 0)
|
||||
{
|
||||
requestData->logoPixelWidth = (uint32_t) imageData.image->columns;
|
||||
requestData->logoPixelHeight = (uint32_t) imageData.image->rows;
|
||||
}
|
||||
else if(requestData->logoPixelWidth == 0)
|
||||
requestData->logoPixelWidth = (uint32_t) ((double) imageData.image->columns / (double) imageData.image->rows * requestData->logoPixelHeight);
|
||||
else if(requestData->logoPixelHeight == 0)
|
||||
requestData->logoPixelHeight = (uint32_t) ((double) imageData.image->rows / (double) imageData.image->columns * requestData->logoPixelWidth);
|
||||
|
||||
requestData->logoCharacterWidth = simpleCeil((double) requestData->logoPixelWidth / requestData->characterPixelWidth);
|
||||
requestData->logoCharacterHeight = simpleCeil((double) requestData->logoPixelHeight / requestData->characterPixelHeight);
|
||||
|
||||
if(requestData->logoPixelWidth == 0 || requestData->logoPixelHeight == 0 || requestData->logoCharacterWidth == 0 || requestData->logoCharacterHeight == 0)
|
||||
{
|
||||
ffDestroyImage(imageData.image);
|
||||
ffDestroyExceptionInfo(imageData.exceptionInfo);
|
||||
return FF_LOGO_IMAGE_RESULT_RUN_ERROR;
|
||||
}
|
||||
|
||||
Image* resized = imData->resizeFunc(imageData.image, requestData->logoPixelWidth, requestData->logoPixelHeight, imageData.exceptionInfo);
|
||||
ffDestroyImage(imageData.image);
|
||||
if(resized == NULL)
|
||||
{
|
||||
ffDestroyExceptionInfo(imageData.exceptionInfo);
|
||||
return FF_LOGO_IMAGE_RESULT_RUN_ERROR;
|
||||
}
|
||||
imageData.image = resized;
|
||||
|
||||
imageData.imageInfo = ffAcquireImageInfo();
|
||||
if(imageData.imageInfo == NULL)
|
||||
{
|
||||
ffDestroyImage(imageData.image);
|
||||
ffDestroyExceptionInfo(imageData.exceptionInfo);
|
||||
return FF_LOGO_IMAGE_RESULT_RUN_ERROR;
|
||||
}
|
||||
|
||||
bool printSuccessful = false;
|
||||
if(requestData->type == FF_LOGO_TYPE_CHAFA)
|
||||
{
|
||||
#ifdef FF_HAVE_CHAFA
|
||||
printSuccessful = printImageChafa(instance, requestData, &imageData);
|
||||
#endif
|
||||
}
|
||||
else if(requestData->type == FF_LOGO_TYPE_KITTY)
|
||||
printSuccessful = printImageKitty(instance, requestData, &imageData);
|
||||
else if(requestData->type == FF_LOGO_TYPE_SIXEL)
|
||||
printSuccessful = printImageSixel(instance, requestData, &imageData);
|
||||
|
||||
ffDestroyImageInfo(imageData.imageInfo);
|
||||
ffDestroyImage(imageData.image);
|
||||
ffDestroyExceptionInfo(imageData.exceptionInfo);
|
||||
|
||||
return printSuccessful ? FF_LOGO_IMAGE_RESULT_SUCCESS : FF_LOGO_IMAGE_RESULT_RUN_ERROR;
|
||||
}
|
||||
|
||||
static int getCacheFD(FFLogoRequestData* requestData, const char* fileName)
|
||||
{
|
||||
uint32_t cacheDirLength = requestData->cacheDir.length;
|
||||
ffStrbufAppendS(&requestData->cacheDir, fileName);
|
||||
int fd = open(requestData->cacheDir.chars, O_RDONLY);
|
||||
ffStrbufSubstrBefore(&requestData->cacheDir, cacheDirLength);
|
||||
return fd;
|
||||
}
|
||||
|
||||
static void readCachedStrbuf(FFLogoRequestData* requestData, FFstrbuf* result, const char* cacheFileName)
|
||||
{
|
||||
uint32_t cacheDirLength = requestData->cacheDir.length;
|
||||
ffStrbufAppendS(&requestData->cacheDir, cacheFileName);
|
||||
ffAppendFileContent(requestData->cacheDir.chars, result);
|
||||
ffStrbufSubstrBefore(&requestData->cacheDir, cacheDirLength);
|
||||
}
|
||||
|
||||
static uint32_t readCachedUint32(FFLogoRequestData* requestData, const char* cacheFileName)
|
||||
{
|
||||
FFstrbuf content;
|
||||
ffStrbufInit(&content);
|
||||
readCachedStrbuf(requestData, &content, cacheFileName);
|
||||
|
||||
uint32_t result = 0;
|
||||
|
||||
if(content.length != sizeof(result))
|
||||
{
|
||||
ffStrbufDestroy(&content);
|
||||
return 0;
|
||||
}
|
||||
|
||||
memcpy(&result, content.chars, sizeof(result));
|
||||
|
||||
ffStrbufDestroy(&content);
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
static bool printCachedChars(FFinstance* instance, FFLogoRequestData* requestData)
|
||||
{
|
||||
FFstrbuf content;
|
||||
ffStrbufInitA(&content, 32768);
|
||||
|
||||
if(requestData->type == FF_LOGO_TYPE_CHAFA)
|
||||
readCachedStrbuf(requestData, &content, FF_CACHE_FILE_CHAFA);
|
||||
|
||||
if(content.length == 0)
|
||||
{
|
||||
ffStrbufDestroy(&content);
|
||||
return false;
|
||||
}
|
||||
|
||||
ffLogoPrint(instance, content.chars, false);
|
||||
ffStrbufDestroy(&content);
|
||||
return true;
|
||||
}
|
||||
|
||||
static bool printCachedPixel(FFinstance* instance, FFLogoRequestData* requestData)
|
||||
{
|
||||
requestData->logoCharacterWidth = instance->config.logoWidth;
|
||||
if(requestData->logoCharacterWidth == 0)
|
||||
{
|
||||
requestData->logoCharacterWidth = readCachedUint32(requestData, FF_CACHE_FILE_WIDTH);
|
||||
if(requestData->logoCharacterWidth == 0)
|
||||
return false;
|
||||
}
|
||||
|
||||
requestData->logoCharacterHeight = instance->config.logoHeight;
|
||||
if(requestData->logoCharacterHeight == 0)
|
||||
{
|
||||
requestData->logoCharacterHeight = readCachedUint32(requestData, FF_CACHE_FILE_HEIGHT);
|
||||
if(requestData->logoCharacterHeight == 0)
|
||||
return false;
|
||||
}
|
||||
|
||||
int fd = -1;
|
||||
if(requestData->type == FF_LOGO_TYPE_KITTY)
|
||||
{
|
||||
fd = getCacheFD(requestData, FF_CACHE_FILE_KITTY_COMPRESSED);
|
||||
if(fd == -1)
|
||||
fd = getCacheFD(requestData, FF_CACHE_FILE_KITTY_UNCOMPRESSED);
|
||||
}
|
||||
else if(requestData->type == FF_LOGO_TYPE_SIXEL)
|
||||
fd = getCacheFD(requestData, FF_CACHE_FILE_SIXEL);
|
||||
|
||||
if(fd == -1)
|
||||
return false;
|
||||
|
||||
ffPrintCharTimes(' ', instance->config.logoPaddingLeft);
|
||||
fflush(stdout);
|
||||
|
||||
char buffer[32768];
|
||||
ssize_t readBytes;
|
||||
while((readBytes = read(fd, buffer, sizeof(buffer))) > 0)
|
||||
FF_UNUSED(write(STDOUT_FILENO, buffer, (size_t) readBytes));
|
||||
|
||||
close(fd);
|
||||
|
||||
instance->state.logoWidth = requestData->logoCharacterWidth + instance->config.logoPaddingLeft + instance->config.logoPaddingRight;
|
||||
instance->state.logoHeight = requestData->logoCharacterHeight;
|
||||
|
||||
//Go to upper left corner
|
||||
fputs("\033[9999999D", stdout);
|
||||
printf("\033[%uA", instance->state.logoHeight);
|
||||
return true;
|
||||
}
|
||||
|
||||
static bool printCached(FFinstance* instance, FFLogoRequestData* requestData)
|
||||
{
|
||||
if(requestData->type == FF_LOGO_TYPE_CHAFA)
|
||||
return printCachedChars(instance, requestData);
|
||||
else
|
||||
return printCachedPixel(instance, requestData);
|
||||
}
|
||||
|
||||
static bool getCharacterPixelDimensions(FFLogoRequestData* requestData)
|
||||
{
|
||||
struct winsize winsize;
|
||||
|
||||
//Initialize every member to 0, because it isn't guaranteed that every terminal sets them all
|
||||
memset(&winsize, 0, sizeof(struct winsize));
|
||||
|
||||
ioctl(STDOUT_FILENO, TIOCGWINSZ, &winsize);
|
||||
|
||||
if(winsize.ws_row == 0 || winsize.ws_col == 0)
|
||||
ffGetTerminalResponse("\033[18t", "\033[8;%hu;%hut", &winsize.ws_row, &winsize.ws_col);
|
||||
|
||||
if(winsize.ws_row == 0 || winsize.ws_col == 0)
|
||||
return false;
|
||||
|
||||
if(winsize.ws_ypixel == 0 || winsize.ws_xpixel == 0)
|
||||
ffGetTerminalResponse("\033[14t", "\033[4;%hu;%hut", &winsize.ws_ypixel, &winsize.ws_xpixel);
|
||||
|
||||
requestData->characterPixelWidth = winsize.ws_xpixel / (double) winsize.ws_col;
|
||||
requestData->characterPixelHeight = winsize.ws_ypixel / (double) winsize.ws_row;
|
||||
|
||||
return requestData->characterPixelWidth > 1.0 && requestData->characterPixelHeight > 1.0;
|
||||
}
|
||||
|
||||
FFLogoImageResult ffLogoPrintImageIfExists(FFinstance* instance, FFLogoType type)
|
||||
{
|
||||
//Performance optimisation
|
||||
#ifndef FF_HAVE_CHAFA
|
||||
if(type == FF_LOGO_TYPE_CHAFA)
|
||||
return FF_LOGO_IMAGE_RESULT_INIT_ERROR;
|
||||
#endif
|
||||
|
||||
FFLogoRequestData requestData;
|
||||
requestData.type = type;
|
||||
requestData.characterPixelWidth = 1;
|
||||
requestData.characterPixelHeight = 1;
|
||||
|
||||
if(
|
||||
(type != FF_LOGO_TYPE_CHAFA || instance->config.logoWidth == 0 || instance->config.logoHeight == 0) &&
|
||||
!getCharacterPixelDimensions(&requestData)
|
||||
)
|
||||
return FF_LOGO_IMAGE_RESULT_RUN_ERROR;
|
||||
|
||||
requestData.logoPixelWidth = simpleCeil((double) instance->config.logoWidth * requestData.characterPixelWidth);
|
||||
requestData.logoPixelHeight = simpleCeil((double) instance->config.logoHeight * requestData.characterPixelHeight);
|
||||
|
||||
ffStrbufInitA(&requestData.cacheDir, PATH_MAX * 2);
|
||||
ffStrbufAppend(&requestData.cacheDir, &instance->state.cacheDir);
|
||||
ffStrbufAppendS(&requestData.cacheDir, "images");
|
||||
|
||||
ffStrbufEnsureFree(&requestData.cacheDir, PATH_MAX);
|
||||
if(realpath(instance->config.logoSource.chars, requestData.cacheDir.chars + requestData.cacheDir.length) == NULL)
|
||||
{
|
||||
//We can safely return here, because if realpath failed, we surely won't be able to read the file
|
||||
ffStrbufDestroy(&requestData.cacheDir);
|
||||
return FF_LOGO_IMAGE_RESULT_RUN_ERROR;
|
||||
}
|
||||
ffStrbufRecalculateLength(&requestData.cacheDir);
|
||||
if(!ffStrbufEndsWithC(&requestData.cacheDir, '/'))
|
||||
ffStrbufAppendC(&requestData.cacheDir, '/');
|
||||
|
||||
ffStrbufAppendF(&requestData.cacheDir, "%u", requestData.logoPixelWidth);
|
||||
ffStrbufAppendC(&requestData.cacheDir, '*');
|
||||
ffStrbufAppendF(&requestData.cacheDir, "%u", requestData.logoPixelHeight);
|
||||
ffStrbufAppendC(&requestData.cacheDir, '/');
|
||||
|
||||
if(!instance->config.recache && printCached(instance, &requestData))
|
||||
{
|
||||
ffStrbufDestroy(&requestData.cacheDir);
|
||||
return FF_LOGO_IMAGE_RESULT_SUCCESS;
|
||||
}
|
||||
|
||||
FFLogoImageResult result = FF_LOGO_IMAGE_RESULT_INIT_ERROR;
|
||||
|
||||
#ifdef FF_HAVE_IMAGEMAGICK7
|
||||
result = ffLogoPrintImageIM7(instance, &requestData);
|
||||
#endif
|
||||
|
||||
#ifdef FF_HAVE_IMAGEMAGICK6
|
||||
if(result == FF_LOGO_IMAGE_RESULT_INIT_ERROR)
|
||||
result = ffLogoPrintImageIM6(instance, &requestData);
|
||||
#endif
|
||||
|
||||
ffStrbufDestroy(&requestData.cacheDir);
|
||||
return result;
|
||||
}
|
||||
|
||||
#else //FF_HAVE_IMAGEMAGICK{6, 7}
|
||||
FFLogoImageResult ffLogoPrintImageIfExists(FFinstance* instance, FFLogoType type)
|
||||
{
|
||||
FF_UNUSED(instance, type);
|
||||
return FF_LOGO_IMAGE_RESULT_RUN_ERROR;
|
||||
}
|
||||
#endif //FF_HAVE_IMAGEMAGICK{6, 7}
|
||||
@@ -0,0 +1,48 @@
|
||||
#pragma once
|
||||
|
||||
#ifndef FF_INCLUDED_LOGO_SIXEL_sixel
|
||||
#define FF_INCLUDED_LOGO_SIXEL_sixel
|
||||
|
||||
#include "../logo.h"
|
||||
|
||||
#if defined(FF_HAVE_IMAGEMAGICK7) || defined(FF_HAVE_IMAGEMAGICK6)
|
||||
|
||||
#define MAGICKCORE_HDRI_ENABLE 1
|
||||
#define MAGICKCORE_QUANTUM_DEPTH 16
|
||||
|
||||
typedef struct FFLogoRequestData
|
||||
{
|
||||
FFLogoType type;
|
||||
FFstrbuf cacheDir;
|
||||
|
||||
double characterPixelWidth;
|
||||
double characterPixelHeight;
|
||||
|
||||
uint32_t logoPixelWidth;
|
||||
uint32_t logoPixelHeight;
|
||||
|
||||
uint32_t logoCharacterHeight;
|
||||
uint32_t logoCharacterWidth;
|
||||
} FFLogoRequestData;
|
||||
|
||||
typedef struct FFIMData
|
||||
{
|
||||
void* library;
|
||||
void*(*resizeFunc)(const void* image, size_t width, size_t height, void* exceptionInfo);
|
||||
} FFIMData;
|
||||
|
||||
FFLogoImageResult ffLogoPrintImageImpl(FFinstance* instance, FFLogoRequestData* requestData, const FFIMData* imData);
|
||||
|
||||
#endif
|
||||
|
||||
#ifdef FF_HAVE_IMAGEMAGICK7
|
||||
FFLogoImageResult ffLogoPrintImageIM7(FFinstance* instance, FFLogoRequestData* requestData);
|
||||
#endif
|
||||
|
||||
#ifdef FF_HAVE_IMAGEMAGICK6
|
||||
#include <math.h>
|
||||
FFLogoImageResult ffLogoPrintImageIM6(FFinstance* instance, FFLogoRequestData* requestData);
|
||||
#endif
|
||||
|
||||
|
||||
#endif
|
||||
+281
@@ -0,0 +1,281 @@
|
||||
#include "logo.h"
|
||||
|
||||
#include <ctype.h>
|
||||
|
||||
void ffLogoPrint(FFinstance* instance, const char* data, bool doColorReplacement)
|
||||
{
|
||||
uint32_t currentlineLength = 0;
|
||||
|
||||
fputs(FASTFETCH_TEXT_MODIFIER_BOLT, stdout);
|
||||
ffPrintCharTimes(' ', instance->config.logoPaddingLeft);
|
||||
|
||||
//Use logoColor[0] as the default color
|
||||
if(doColorReplacement)
|
||||
ffPrintColor(&instance->config.logoColors[0]);
|
||||
|
||||
while(*data != '\0')
|
||||
{
|
||||
//We are at the end of a line. Print paddings and update max line length
|
||||
if(*data == '\n' || (*data == '\r' && *(data + 1) == '\n'))
|
||||
{
|
||||
ffPrintCharTimes(' ', instance->config.logoPaddingRight);
|
||||
|
||||
//We have \r\n, skip the \r
|
||||
if(*data == '\r')
|
||||
++data;
|
||||
|
||||
putchar('\n');
|
||||
++data;
|
||||
|
||||
ffPrintCharTimes(' ', instance->config.logoPaddingLeft);
|
||||
|
||||
if(currentlineLength > instance->state.logoWidth)
|
||||
instance->state.logoWidth = currentlineLength;
|
||||
|
||||
currentlineLength = 0;
|
||||
++instance->state.logoHeight;
|
||||
continue;
|
||||
}
|
||||
|
||||
//Always print tabs as 4 spaces, to have consistent spacing
|
||||
if(*data == '\t')
|
||||
{
|
||||
ffPrintCharTimes(' ', 4);
|
||||
++data;
|
||||
continue;
|
||||
}
|
||||
|
||||
//We have an escape sequence direclty as bytes. We print it, but don't increase the line length
|
||||
if(*data == '\033' && *(data + 1) == '[')
|
||||
{
|
||||
const char* start = data;
|
||||
|
||||
fputs("\033[", stdout);
|
||||
data += 2;
|
||||
|
||||
while(isdigit(*data) || *data == ';')
|
||||
putchar(*data++); // number
|
||||
|
||||
//We have a valid control sequence, print it and continue with next char
|
||||
if(isascii(*data))
|
||||
{
|
||||
putchar(*data++); // single letter, end of control sequence
|
||||
continue;
|
||||
}
|
||||
|
||||
//Invalid control sequence, try to get most accurate length
|
||||
currentlineLength += (uint32_t) (data - start - 1); //-1 for \033 which for sure doesn't take any space
|
||||
|
||||
//Don't continue here, print the char after the letters with the unicode printing
|
||||
}
|
||||
|
||||
//We have a fastfetch color placeholder. Replace it with the esacape sequence, don't increase the line length
|
||||
if(doColorReplacement && *data == '$')
|
||||
{
|
||||
++data;
|
||||
|
||||
//If we have $$, or $\0, print it as single $
|
||||
if(*data == '$' || *data == '\0')
|
||||
{
|
||||
putchar('$');
|
||||
++currentlineLength;
|
||||
++data;
|
||||
continue;
|
||||
}
|
||||
|
||||
//Map the number to an array index, so that '1' -> 0, '2' -> 1, etc.
|
||||
int index = ((int) *data) - 49;
|
||||
|
||||
//If the index is valid, print the color. Otherwise continue as normal
|
||||
if(index < 0 || index >= FASTFETCH_LOGO_MAX_COLORS)
|
||||
{
|
||||
putchar('$');
|
||||
++currentlineLength;
|
||||
//Don't continue here, we want to print the current char as unicode
|
||||
}
|
||||
else
|
||||
{
|
||||
ffPrintColor(&instance->config.logoColors[index]);
|
||||
++data;
|
||||
continue;
|
||||
}
|
||||
}
|
||||
|
||||
//Do the printing, respecting unicode
|
||||
|
||||
++currentlineLength;
|
||||
|
||||
int codepoint = (unsigned char) *data;
|
||||
uint8_t bytes;
|
||||
|
||||
if(codepoint <= 127)
|
||||
bytes = 1;
|
||||
else if((codepoint & 0xE0) == 0xC0)
|
||||
bytes = 2;
|
||||
else if((codepoint & 0xF0) == 0xE0)
|
||||
bytes = 3;
|
||||
else if((codepoint & 0xF8) == 0xF0)
|
||||
bytes = 4;
|
||||
else
|
||||
bytes = 1; //Invalid utf8, print it as is, byte by byte
|
||||
|
||||
for(uint8_t i = 0; i < bytes; ++i)
|
||||
{
|
||||
if(*data == '\0')
|
||||
break;
|
||||
|
||||
putchar(*data++);
|
||||
}
|
||||
}
|
||||
|
||||
ffPrintCharTimes(' ', instance->config.logoPaddingRight);
|
||||
fputs(FASTFETCH_TEXT_MODIFIER_RESET, stdout);
|
||||
|
||||
//Happens if the last line is the longest
|
||||
if(currentlineLength > instance->state.logoWidth)
|
||||
instance->state.logoWidth = currentlineLength;
|
||||
|
||||
instance->state.logoWidth += instance->config.logoPaddingLeft + instance->config.logoPaddingRight;
|
||||
|
||||
//Go to the leftmost position
|
||||
fputs("\033[9999999D", stdout);
|
||||
|
||||
//If the logo height is > 1, go up the height
|
||||
if(instance->state.logoHeight > 0)
|
||||
printf("\033[%uA", instance->state.logoHeight);
|
||||
}
|
||||
|
||||
static void logoPrintFile(FFinstance* instance, bool doColorReplacement)
|
||||
{
|
||||
FFstrbuf content;
|
||||
ffStrbufInitA(&content, 2047);
|
||||
|
||||
if(ffAppendFileContent(instance->config.logoSource.chars, &content))
|
||||
ffLogoPrint(instance, content.chars, doColorReplacement);
|
||||
else
|
||||
ffLogoPrintUnknown(instance);
|
||||
}
|
||||
|
||||
static void logoPrintDetected(FFinstance* instance)
|
||||
{
|
||||
//If no logo source is given, detect the logo from OS and print the right builtin one
|
||||
if(instance->config.logoSource.length == 0)
|
||||
{
|
||||
ffLogoPrintBuiltinDetected(instance);
|
||||
return;
|
||||
}
|
||||
|
||||
//If a logo source is given, first look if it is the name of a builtin logo
|
||||
if(ffLogoPrintBuiltinIfExists(instance))
|
||||
return;
|
||||
|
||||
const FFTerminalShellResult* terminalShell = ffDetectTerminalShell(instance);
|
||||
|
||||
FFLogoImageResult imageResult = FF_LOGO_IMAGE_RESULT_INIT_ERROR;
|
||||
|
||||
//Terminals that are known to support kitty graphics protocol
|
||||
//Try to load the logo as image.
|
||||
if(
|
||||
ffStrbufIgnCaseCompS(&terminalShell->terminalProcessName, "kitty") == 0 ||
|
||||
ffStrbufIgnCaseCompS(&terminalShell->terminalProcessName, "konsole") == 0 ||
|
||||
ffStrbufIgnCaseCompS(&terminalShell->terminalProcessName, "wezterm") == 0 ||
|
||||
ffStrbufIgnCaseCompS(&terminalShell->terminalProcessName, "wayst") == 0
|
||||
) {
|
||||
imageResult = ffLogoPrintImageIfExists(instance, FF_LOGO_TYPE_KITTY);
|
||||
if(imageResult == FF_LOGO_IMAGE_RESULT_SUCCESS)
|
||||
return;
|
||||
}
|
||||
|
||||
//If the logo can be loaded as an image, convert it to ascii art.
|
||||
//This happens in all terminals, that don't support kitty graphics protocol
|
||||
if(
|
||||
imageResult != FF_LOGO_IMAGE_RESULT_RUN_ERROR &&
|
||||
ffLogoPrintImageIfExists(instance, FF_LOGO_TYPE_CHAFA) == FF_LOGO_IMAGE_RESULT_SUCCESS
|
||||
) return;
|
||||
|
||||
//Print the content of the file as logo
|
||||
logoPrintFile(instance, true);
|
||||
}
|
||||
|
||||
void ffPrintLogo(FFinstance* instance)
|
||||
{
|
||||
if(instance->config.mainColor.length == 0)
|
||||
ffLogoSetMainColor(instance);
|
||||
|
||||
if( //Logo type needs set logo name, but nothing was given. Print question mark
|
||||
instance->config.logoType != FF_LOGO_TYPE_AUTO &&
|
||||
instance->config.logoSource.length == 0
|
||||
)
|
||||
ffLogoPrintUnknown(instance);
|
||||
else if(instance->config.logoType == FF_LOGO_TYPE_BUILTIN)
|
||||
{
|
||||
if(!ffLogoPrintBuiltinIfExists(instance))
|
||||
ffLogoPrintUnknown(instance);
|
||||
}
|
||||
else if(instance->config.logoType == FF_LOGO_TYPE_FILE)
|
||||
logoPrintFile(instance, true);
|
||||
else if(instance->config.logoType == FF_LOGO_TYPE_RAW)
|
||||
logoPrintFile(instance, false);
|
||||
else if(instance->config.logoType == FF_LOGO_TYPE_SIXEL || instance->config.logoType == FF_LOGO_TYPE_KITTY || instance->config.logoType == FF_LOGO_TYPE_CHAFA)
|
||||
{
|
||||
if(ffLogoPrintImageIfExists(instance, instance->config.logoType) != FF_LOGO_IMAGE_RESULT_SUCCESS)
|
||||
ffLogoPrintBuiltinDetected(instance);
|
||||
}
|
||||
else
|
||||
logoPrintDetected(instance);
|
||||
}
|
||||
|
||||
static inline void printLogoWidth(const FFinstance* instance)
|
||||
{
|
||||
if(instance->state.logoWidth > 0)
|
||||
printf("\033[%uC", instance->state.logoWidth);
|
||||
}
|
||||
|
||||
void ffPrintRemainingLogo(FFinstance* instance)
|
||||
{
|
||||
if(!instance->config.logoPrintRemaining)
|
||||
return;
|
||||
|
||||
for(uint32_t i = instance->state.keysHeight; i <= instance->state.logoHeight; i++)
|
||||
{
|
||||
printLogoWidth(instance);
|
||||
putchar('\n');
|
||||
}
|
||||
}
|
||||
|
||||
void ffPrintLogoLine(FFinstance* instance)
|
||||
{
|
||||
printLogoWidth(instance);
|
||||
++instance->state.keysHeight;
|
||||
fputs(FASTFETCH_TEXT_MODIFIER_RESET, stdout);
|
||||
}
|
||||
|
||||
void ffPrintLogoAndKey(FFinstance* instance, const char* moduleName, uint8_t moduleIndex, const FFstrbuf* customKeyFormat)
|
||||
{
|
||||
ffPrintLogoLine(instance);
|
||||
|
||||
fputs(FASTFETCH_TEXT_MODIFIER_BOLT, stdout);
|
||||
ffPrintColor(&instance->config.mainColor);
|
||||
|
||||
if(customKeyFormat == NULL || customKeyFormat->length == 0)
|
||||
{
|
||||
fputs(moduleName, stdout);
|
||||
|
||||
if(moduleIndex > 0)
|
||||
printf(" %hhu", moduleIndex);
|
||||
}
|
||||
else
|
||||
{
|
||||
FFstrbuf key;
|
||||
ffStrbufInit(&key);
|
||||
ffParseFormatString(&key, customKeyFormat, NULL, 1, (FFformatarg[]){
|
||||
{FF_FORMAT_ARG_TYPE_UINT8, &moduleIndex}
|
||||
});
|
||||
ffStrbufWriteTo(&key, stdout);
|
||||
ffStrbufDestroy(&key);
|
||||
}
|
||||
|
||||
fputs(FASTFETCH_TEXT_MODIFIER_RESET, stdout);
|
||||
ffStrbufWriteTo(&instance->config.separator, stdout);
|
||||
fputs(FASTFETCH_TEXT_MODIFIER_RESET, stdout);
|
||||
}
|
||||
@@ -0,0 +1,28 @@
|
||||
#pragma once
|
||||
|
||||
#ifndef FASTFETCH_INCLUDED_LOGO_logo
|
||||
#define FASTFETCH_INCLUDED_LOGO_logo
|
||||
|
||||
#include "fastfetch.h"
|
||||
|
||||
//logo.c
|
||||
void ffLogoPrint(FFinstance* instance, const char* data, bool doColorReplacement);
|
||||
|
||||
//builtin.c
|
||||
void ffLogoSetMainColor(FFinstance* instance);
|
||||
|
||||
void ffLogoPrintUnknown(FFinstance* instance);
|
||||
bool ffLogoPrintBuiltinIfExists(FFinstance* instance);
|
||||
void ffLogoPrintBuiltinDetected(FFinstance* instance);
|
||||
|
||||
typedef enum FFLogoImageResult
|
||||
{
|
||||
FF_LOGO_IMAGE_RESULT_SUCCESS, //Logo printed
|
||||
FF_LOGO_IMAGE_RESULT_INIT_ERROR, //Failed to load library, try again with next IM version
|
||||
FF_LOGO_IMAGE_RESULT_RUN_ERROR //Failed to load / convert image, cancle whole sixel code
|
||||
} FFLogoImageResult;
|
||||
|
||||
//image/image.c
|
||||
FFLogoImageResult ffLogoPrintImageIfExists(FFinstance* instance, FFLogoType type);
|
||||
|
||||
#endif
|
||||
+7
-7
@@ -42,7 +42,7 @@ void ffPrintCPU(FFinstance* instance)
|
||||
FILE* cpuinfo = fopen("/proc/cpuinfo", "r");
|
||||
if(cpuinfo == NULL)
|
||||
{
|
||||
ffPrintError(instance, FF_CPU_MODULE_NAME, 0, &instance->config.cpuKey, &instance->config.cpuFormat, FF_CPU_NUM_FORMAT_ARGS, "fopen(\"/proc/cpuinfo\", \"r\") == NULL");
|
||||
ffPrintError(instance, FF_CPU_MODULE_NAME, 0, &instance->config.cpuKey, &instance->config.cpuFormat, FF_CPU_NUM_FORMAT_ARGS, "fopen(\"""/proc/cpuinfo\", \"r\") == NULL");
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -68,11 +68,11 @@ void ffPrintCPU(FFinstance* instance)
|
||||
break;
|
||||
|
||||
(void)(
|
||||
ffGetPropValue(line, "model name :", &name) ||
|
||||
ffGetPropValue(line, "vendor_id :", &vendor) ||
|
||||
ffGetPropValue(line, "cpu cores :", &physicalCoresString) ||
|
||||
ffGetPropValue(line, "cpu MHz :", &procGhzString) ||
|
||||
(name.length == 0 && ffGetPropValue(line, "Hardware :", &name)) //For Android devices
|
||||
ffParsePropLine(line, "model name :", &name) ||
|
||||
ffParsePropLine(line, "vendor_id :", &vendor) ||
|
||||
ffParsePropLine(line, "cpu cores :", &physicalCoresString) ||
|
||||
ffParsePropLine(line, "cpu MHz :", &procGhzString) ||
|
||||
(name.length == 0 && ffParsePropLine(line, "Hardware :", &name)) //For Android devices
|
||||
);
|
||||
}
|
||||
|
||||
@@ -164,7 +164,7 @@ void ffPrintCPU(FFinstance* instance)
|
||||
if(ghz > 0)
|
||||
ffStrbufAppendF(&cpu, " @ %.9gGHz", ghz);
|
||||
|
||||
ffPrintAndSaveToCache(instance, FF_CPU_MODULE_NAME, &instance->config.cpuKey, &cpu, &instance->config.cpuFormat, FF_CPU_NUM_FORMAT_ARGS, (FFformatarg[]){
|
||||
ffPrintAndWriteToCache(instance, FF_CPU_MODULE_NAME, &instance->config.cpuKey, &cpu, &instance->config.cpuFormat, FF_CPU_NUM_FORMAT_ARGS, (FFformatarg[]){
|
||||
{FF_FORMAT_ARG_TYPE_STRBUF, &name},
|
||||
{FF_FORMAT_ARG_TYPE_STRBUF, &namePretty},
|
||||
{FF_FORMAT_ARG_TYPE_STRBUF, &vendor},
|
||||
|
||||
@@ -12,7 +12,7 @@ void ffPrintCPUUsage(FFinstance* instance)
|
||||
FILE* procStat = fopen("/proc/stat", "r");
|
||||
if(procStat == NULL)
|
||||
{
|
||||
ffPrintError(instance, FF_CPU_USAGE_MODULE_NAME, 0, &instance->config.cpuUsageKey, &instance->config.cpuUsageFormat, FF_CPU_USAGE_NUM_FORMAT_ARGS, "fopen(\"/proc/stat\", \"r\") == NULL");
|
||||
ffPrintError(instance, FF_CPU_USAGE_MODULE_NAME, 0, &instance->config.cpuUsageKey, &instance->config.cpuUsageFormat, FF_CPU_USAGE_NUM_FORMAT_ARGS, "fopen(\"""/proc/stat\", \"r\") == NULL");
|
||||
return;
|
||||
}
|
||||
if (fscanf(procStat, "cpu%ld%ld%ld%ld%ld%ld%ld", &user, &nice, &system, &idle, &iowait, &irq, &softirq) < 0) goto exit;
|
||||
|
||||
@@ -147,7 +147,7 @@ static bool printCursorFromXDG(FFinstance* instance, bool user)
|
||||
if(user)
|
||||
ffParsePropFileHome(instance, ".icons/default/index.theme", "Inherits =", &theme);
|
||||
else
|
||||
ffParsePropFile("/usr/share/icons/default/index.theme", "Inherits =", &theme);
|
||||
ffParsePropFile(FASTFETCH_TARGET_DIR_USR"/share/icons/default/index.theme", "Inherits =", &theme);
|
||||
|
||||
if(theme.length == 0)
|
||||
{
|
||||
|
||||
+3
-4
@@ -79,18 +79,17 @@ void ffPrintDisk(FFinstance* instance)
|
||||
{
|
||||
if(instance->config.diskFolders.length == 0)
|
||||
{
|
||||
|
||||
struct statvfs fsRoot;
|
||||
int rootRet = statvfs("/", &fsRoot);
|
||||
|
||||
struct statvfs fsHome;
|
||||
int homeRet = statvfs("/home", &fsHome);
|
||||
int homeRet = statvfs(FASTFETCH_TARGET_DIR_HOME, &fsHome);
|
||||
|
||||
if(rootRet != 0 && homeRet != 0)
|
||||
{
|
||||
FF_STRBUF_CREATE(key);
|
||||
getKey(instance, &key, "", false);
|
||||
ffPrintError(instance, key.chars, 0, NULL, &instance->config.diskFormat, FF_DISK_NUM_FORMAT_ARGS, "statvfs failed for both / and /home");
|
||||
ffPrintError(instance, key.chars, 0, NULL, &instance->config.diskFormat, FF_DISK_NUM_FORMAT_ARGS, "statvfs failed for both / and " FASTFETCH_TARGET_DIR_HOME);
|
||||
ffStrbufDestroy(&key);
|
||||
return;
|
||||
}
|
||||
@@ -99,7 +98,7 @@ void ffPrintDisk(FFinstance* instance)
|
||||
printStatvfsCreateKey(instance, "/", &fsRoot);
|
||||
|
||||
if(homeRet == 0 && (rootRet != 0 || fsRoot.f_fsid != fsHome.f_fsid))
|
||||
printStatvfsCreateKey(instance, "/home", &fsHome);
|
||||
printStatvfsCreateKey(instance, FASTFETCH_TARGET_DIR_HOME, &fsHome);
|
||||
}
|
||||
else
|
||||
{
|
||||
|
||||
+1
-1
@@ -43,7 +43,7 @@ void ffPrintFont(FFinstance* instance)
|
||||
|
||||
FFstrbuf gtk;
|
||||
ffStrbufInitA(>k, 64);
|
||||
ffGetGtkPretty(>k, >k2.pretty, >k3.pretty, >k4.pretty);
|
||||
ffParseGTK(>k, >k2.pretty, >k3.pretty, >k4.pretty);
|
||||
|
||||
if(instance->config.fontFormat.length == 0)
|
||||
{
|
||||
|
||||
+2
-2
@@ -17,7 +17,7 @@ typedef struct GPUResult
|
||||
|
||||
static void vulkanFillGPUs(FFinstance* instance, FFlist* results)
|
||||
{
|
||||
FF_LIBRARY_LOAD(vulkan, instance->config.libVulkan, , "libvulkan.so", "libvulkan.so.1")
|
||||
FF_LIBRARY_LOAD(vulkan, instance->config.libVulkan, , "libvulkan.so", 2)
|
||||
FF_LIBRARY_LOAD_SYMBOL(vulkan, vkCreateInstance,)
|
||||
FF_LIBRARY_LOAD_SYMBOL(vulkan, vkDestroyInstance,)
|
||||
FF_LIBRARY_LOAD_SYMBOL(vulkan, vkEnumeratePhysicalDevices,)
|
||||
@@ -133,7 +133,7 @@ static void pciGetDriver(struct pci_dev* dev, FFstrbuf* driver, char*(*ffpci_get
|
||||
|
||||
static void pciFillGPUs(FFinstance* instance, FFlist* results)
|
||||
{
|
||||
FF_LIBRARY_LOAD(pci, instance->config.libPCI, , "libpci.so", "libpci.so.3")
|
||||
FF_LIBRARY_LOAD(pci, instance->config.libPCI, , "libpci.so", 4)
|
||||
FF_LIBRARY_LOAD_SYMBOL(pci, pci_alloc,)
|
||||
FF_LIBRARY_LOAD_SYMBOL(pci, pci_init,)
|
||||
FF_LIBRARY_LOAD_SYMBOL(pci, pci_scan_bus,)
|
||||
|
||||
+1
-1
@@ -119,7 +119,7 @@ void ffPrintHost(FFinstance* instance)
|
||||
ffStrbufAppend(&host, &version);
|
||||
}
|
||||
|
||||
ffPrintAndSaveToCache(instance, FF_HOST_MODULE_NAME, &instance->config.hostKey, &host, &instance->config.hostFormat, FF_HOST_NUM_FORMAT_ARGS, (FFformatarg[]) {
|
||||
ffPrintAndWriteToCache(instance, FF_HOST_MODULE_NAME, &instance->config.hostKey, &host, &instance->config.hostFormat, FF_HOST_NUM_FORMAT_ARGS, (FFformatarg[]) {
|
||||
{FF_FORMAT_ARG_TYPE_STRBUF, &family},
|
||||
{FF_FORMAT_ARG_TYPE_STRBUF, &name},
|
||||
{FF_FORMAT_ARG_TYPE_STRBUF, &version}
|
||||
|
||||
+1
-1
@@ -30,7 +30,7 @@ void ffPrintIcons(FFinstance* instance)
|
||||
}
|
||||
|
||||
FF_STRBUF_CREATE(gtkPretty);
|
||||
ffGetGtkPretty(>kPretty, gtk2, gtk3, gtk4);
|
||||
ffParseGTK(>kPretty, gtk2, gtk3, gtk4);
|
||||
|
||||
if(instance->config.iconsFormat.length == 0)
|
||||
{
|
||||
|
||||
@@ -28,7 +28,7 @@ void ffPrintLocale(FFinstance* instance)
|
||||
FFstrbuf locale;
|
||||
ffStrbufInit(&locale);
|
||||
|
||||
ffParsePropFile("/etc/locale.conf", "LANG =", &locale);
|
||||
ffParsePropFile(FASTFETCH_TARGET_DIR_ROOT"/etc/locale.conf", "LANG =", &locale);
|
||||
|
||||
if (locale.length == 0)
|
||||
getLocaleFromEnv(&locale);
|
||||
@@ -39,7 +39,7 @@ void ffPrintLocale(FFinstance* instance)
|
||||
return;
|
||||
}
|
||||
|
||||
ffPrintAndSaveToCache(instance, FF_LOCALE_MODULE_NAME, &instance->config.localeKey, &locale, &instance->config.localeFormat, FF_LOCALE_NUM_FORMAT_ARGS, (FFformatarg[]){
|
||||
ffPrintAndWriteToCache(instance, FF_LOCALE_MODULE_NAME, &instance->config.localeKey, &locale, &instance->config.localeFormat, FF_LOCALE_NUM_FORMAT_ARGS, (FFformatarg[]){
|
||||
{FF_FORMAT_ARG_TYPE_STRBUF, &locale}
|
||||
});
|
||||
|
||||
|
||||
@@ -8,7 +8,7 @@ void ffPrintMemory(FFinstance* instance)
|
||||
{
|
||||
FILE* meminfo = fopen("/proc/meminfo", "r");
|
||||
if(meminfo == NULL) {
|
||||
ffPrintError(instance, FF_MEMORY_MODULE_NAME, 0, &instance->config.memoryKey, &instance->config.memoryFormat, FF_MEMORY_NUM_FORMAT_ARGS, "fopen(\"/proc/meminfo\", \"r\") == NULL");
|
||||
ffPrintError(instance, FF_MEMORY_MODULE_NAME, 0, &instance->config.memoryKey, &instance->config.memoryFormat, FF_MEMORY_NUM_FORMAT_ARGS, "fopen(\"""/proc/meminfo\", \"r\") == NULL");
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
+1
-1
@@ -69,7 +69,7 @@ void ffPrintOS(FFinstance* instance)
|
||||
ffStrbufAppendC(&os, ']');
|
||||
}
|
||||
|
||||
ffPrintAndSaveToCache(instance, FF_OS_MODULE_NAME, &instance->config.osKey, &os, &instance->config.osFormat, FF_OS_NUM_FORMAT_ARGS, (FFformatarg[]){
|
||||
ffPrintAndWriteToCache(instance, FF_OS_MODULE_NAME, &instance->config.osKey, &os, &instance->config.osFormat, FF_OS_NUM_FORMAT_ARGS, (FFformatarg[]){
|
||||
{FF_FORMAT_ARG_TYPE_STRBUF, &result->systemName},
|
||||
{FF_FORMAT_ARG_TYPE_STRBUF, &result->name},
|
||||
{FF_FORMAT_ARG_TYPE_STRBUF, &result->prettyName},
|
||||
|
||||
+83
-56
@@ -6,44 +6,6 @@
|
||||
#define FF_PACKAGES_MODULE_NAME "Packages"
|
||||
#define FF_PACKAGES_NUM_FORMAT_ARGS 9
|
||||
|
||||
#ifdef FF_HAVE_RPM
|
||||
#include <rpm/rpmlib.h>
|
||||
#include <rpm/rpmts.h>
|
||||
#include <rpm/rpmdb.h>
|
||||
#include <rpm/rpmlog.h>
|
||||
|
||||
static uint32_t getRpmPackageCount(FFinstance* instance)
|
||||
{
|
||||
FF_LIBRARY_LOAD(rpm, instance->config.librpm, 0, "librpm.so", "librpm.so.4")
|
||||
FF_LIBRARY_LOAD_SYMBOL(rpm, rpmReadConfigFiles, 0)
|
||||
FF_LIBRARY_LOAD_SYMBOL(rpm, rpmtsCreate, 0)
|
||||
FF_LIBRARY_LOAD_SYMBOL(rpm, rpmtsInitIterator, 0)
|
||||
FF_LIBRARY_LOAD_SYMBOL(rpm, rpmdbGetIteratorCount, 0)
|
||||
FF_LIBRARY_LOAD_SYMBOL(rpm, rpmdbFreeIterator, 0)
|
||||
FF_LIBRARY_LOAD_SYMBOL(rpm, rpmtsFree, 0)
|
||||
FF_LIBRARY_LOAD_SYMBOL(rpm, rpmlogSetMask, 0)
|
||||
|
||||
// Don't print any error messages
|
||||
ffrpmlogSetMask(RPMLOG_MASK(RPMLOG_EMERG));
|
||||
|
||||
uint32_t count = 0;
|
||||
rpmts ts = NULL;
|
||||
rpmdbMatchIterator mi = NULL;
|
||||
|
||||
if (ffrpmReadConfigFiles(NULL, NULL)) goto exit;
|
||||
if (!(ts = ffrpmtsCreate())) goto exit;
|
||||
if (!(mi = ffrpmtsInitIterator(ts, RPMDBI_LABEL, NULL, 0))) goto exit;
|
||||
count = (uint32_t) ffrpmdbGetIteratorCount(mi);
|
||||
|
||||
exit:
|
||||
if (mi) ffrpmdbFreeIterator(mi);
|
||||
if (ts) ffrpmtsFree(ts);
|
||||
dlclose(rpm);
|
||||
return count;
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
static uint32_t getNumElements(const char* dirname, unsigned char type)
|
||||
{
|
||||
DIR* dirp = opendir(dirname);
|
||||
@@ -136,31 +98,82 @@ static uint32_t countFilesIn(const char* dirname, const char* filename)
|
||||
return result;
|
||||
}
|
||||
|
||||
static uint32_t getNixPackages(const FFstrbuf* path)
|
||||
{
|
||||
FFstrbuf output;
|
||||
ffStrbufInitA(&output, 128);
|
||||
|
||||
ffProcessAppendStdOut(&output, (char* const[]) {
|
||||
"nix-store",
|
||||
"-qR",
|
||||
path->chars,
|
||||
NULL
|
||||
});
|
||||
|
||||
//Each package is a new line in the output. If at least one line is found, add 1 for the last line.
|
||||
uint32_t result = ffStrbufCountC(&output, '\n');
|
||||
if(result > 0)
|
||||
result++;
|
||||
|
||||
ffStrbufDestroy(&output);
|
||||
return result;
|
||||
}
|
||||
|
||||
static uint32_t getNixPackagesDefault()
|
||||
{
|
||||
FFstrbuf path;
|
||||
ffStrbufInitA(&path, 64);
|
||||
ffStrbufAppendS(&path, FASTFETCH_TARGET_DIR_ROOT"/nix/var/nix/profiles/default");
|
||||
uint32_t result = getNixPackages(&path);
|
||||
ffStrbufDestroy(&path);
|
||||
return result;
|
||||
}
|
||||
|
||||
static uint32_t getNixPackagesUser(const FFinstance* instance)
|
||||
{
|
||||
FFstrbuf path;
|
||||
ffStrbufInitA(&path, 64);
|
||||
ffStrbufAppendS(&path, instance->state.passwd->pw_dir);
|
||||
ffStrbufAppendS(&path, "/.nix-profile");
|
||||
uint32_t result = getNixPackages(&path);
|
||||
ffStrbufDestroy(&path);
|
||||
return result;
|
||||
}
|
||||
|
||||
void ffPrintPackages(FFinstance* instance)
|
||||
{
|
||||
uint32_t pacman = getNumElements("/var/lib/pacman/local", DT_DIR);
|
||||
uint32_t dpkg = getNumStrings("/var/lib/dpkg/status", "Status: ");
|
||||
uint32_t pacman = getNumElements(FASTFETCH_TARGET_DIR_ROOT"/var/lib/pacman/local", DT_DIR);
|
||||
uint32_t dpkg = getNumStrings(FASTFETCH_TARGET_DIR_ROOT"/var/lib/dpkg/status", "Status: ");
|
||||
|
||||
#if __ANDROID__
|
||||
dpkg += getNumStrings("/data/data/com.termux/files/usr/var/lib/dpkg/status", "Status: ");
|
||||
#endif
|
||||
// We do our own sql querys instead of using (lib)rpm, because:
|
||||
// - simply faster
|
||||
// - we get package count on SUSE platforms
|
||||
// - we don't need to regulary increase librpm version, which increases rapidly without providing a major version symlink
|
||||
uint32_t rpm = (uint32_t) ffSettingsGetSQLite3Int(instance, "/var/lib/rpm/rpmdb.sqlite", "SELECT count(blob) FROM Packages");
|
||||
if(rpm == 0)
|
||||
rpm = (uint32_t) ffSettingsGetSQLite3Int(instance, "/var/cache/dnf/packages.db", "SELECT count(pkg) FROM installed");
|
||||
|
||||
#ifdef FF_HAVE_RPM
|
||||
uint32_t rpm = getRpmPackageCount(instance);
|
||||
#else
|
||||
uint32_t rpm = 0;
|
||||
#endif
|
||||
uint32_t emerge = countFilesIn(FASTFETCH_TARGET_DIR_ROOT"/var/db/pkg", "SIZE");
|
||||
uint32_t xbps = getNumElements(FASTFETCH_TARGET_DIR_ROOT"/var/db/xbps", DT_REG);
|
||||
|
||||
uint32_t emerge = countFilesIn("/var/db/pkg", "SIZE");
|
||||
uint32_t xbps = getNumElements("/var/db/xbps", DT_REG);
|
||||
uint32_t flatpak = getNumElements("/var/lib/flatpak/app", DT_DIR);
|
||||
uint32_t snap = getNumElements("/snap", DT_DIR);
|
||||
uint32_t nixUser = 0;
|
||||
uint32_t nixDefault = 0;
|
||||
|
||||
//Nix detection is kinda slow, so we only do it if the nix dir exists
|
||||
if(ffFileExists(FASTFETCH_TARGET_DIR_ROOT"/nix", S_IFDIR))
|
||||
{
|
||||
nixUser = getNixPackagesUser(instance);
|
||||
nixDefault = getNixPackagesDefault();
|
||||
}
|
||||
|
||||
uint32_t flatpak = getNumElements(FASTFETCH_TARGET_DIR_ROOT"/var/lib/flatpak/app", DT_DIR);
|
||||
uint32_t snap = getNumElements(FASTFETCH_TARGET_DIR_ROOT"/snap", DT_DIR);
|
||||
|
||||
//Accounting for the /snap/bin folder
|
||||
if(snap > 0)
|
||||
--snap;
|
||||
|
||||
uint32_t all = pacman + dpkg + rpm + emerge + xbps + flatpak + snap;
|
||||
uint32_t all = pacman + dpkg + rpm + emerge + xbps + nixUser + nixDefault + flatpak + snap;
|
||||
|
||||
if(all == 0)
|
||||
{
|
||||
@@ -170,12 +183,12 @@ void ffPrintPackages(FFinstance* instance)
|
||||
|
||||
FFstrbuf manjaroBranch;
|
||||
ffStrbufInit(&manjaroBranch);
|
||||
if(ffParsePropFile("/etc/pacman-mirrors.conf", "Branch =", &manjaroBranch) && manjaroBranch.length == 0)
|
||||
if(ffParsePropFile(FASTFETCH_TARGET_DIR_ROOT"/etc/pacman-mirrors.conf", "Branch =", &manjaroBranch) && manjaroBranch.length == 0)
|
||||
ffStrbufSetS(&manjaroBranch, "stable");
|
||||
|
||||
if(instance->config.packagesFormat.length == 0)
|
||||
{
|
||||
ffPrintLogoAndKey(instance, FF_PACKAGES_MODULE_NAME, 0, &instance->config.batteryKey);
|
||||
ffPrintLogoAndKey(instance, FF_PACKAGES_MODULE_NAME, 0, &instance->config.packagesKey);
|
||||
|
||||
#define FF_PRINT_PACKAGE(name) \
|
||||
if(name > 0) \
|
||||
@@ -197,7 +210,21 @@ void ffPrintPackages(FFinstance* instance)
|
||||
FF_PRINT_PACKAGE(dpkg)
|
||||
FF_PRINT_PACKAGE(rpm)
|
||||
FF_PRINT_PACKAGE(emerge)
|
||||
FF_PRINT_PACKAGE(xbps)
|
||||
|
||||
if(nixUser > 0)
|
||||
{
|
||||
printf("%u (nix-user)", nixUser);
|
||||
if((all = all - nixUser) > 0)
|
||||
printf(", ");
|
||||
}
|
||||
|
||||
if(nixDefault > 0)
|
||||
{
|
||||
printf("%u (nix-default)", nixDefault);
|
||||
if((all = all - nixDefault) > 0)
|
||||
printf(", ");
|
||||
}
|
||||
|
||||
FF_PRINT_PACKAGE(flatpak)
|
||||
FF_PRINT_PACKAGE(snap)
|
||||
|
||||
|
||||
+39
-2
@@ -1,5 +1,7 @@
|
||||
#include "fastfetch.h"
|
||||
|
||||
#include <ctype.h>
|
||||
|
||||
#define FF_PLAYER_MODULE_NAME "Media Player"
|
||||
#define FF_PLAYER_NUM_FORMAT_ARGS 3
|
||||
|
||||
@@ -13,15 +15,50 @@ void ffPrintPlayer(FFinstance* instance)
|
||||
return;
|
||||
}
|
||||
|
||||
FFstrbuf playerPretty;
|
||||
ffStrbufInit(&playerPretty);
|
||||
|
||||
//If we are on a website, prepend the website name
|
||||
if(ffStrbufStartsWithS(&media->url, "https://www."))
|
||||
ffStrbufAppendS(&playerPretty, media->url.chars + 12);
|
||||
else if(ffStrbufStartsWithS(&media->url, "http://www."))
|
||||
ffStrbufAppendS(&playerPretty, media->url.chars + 11);
|
||||
else if(ffStrbufStartsWithS(&media->url, "https://"))
|
||||
ffStrbufAppendS(&playerPretty, media->url.chars + 8);
|
||||
else if(ffStrbufStartsWithS(&media->url, "http://"))
|
||||
ffStrbufAppendS(&playerPretty, media->url.chars + 7);
|
||||
|
||||
//If we found a website name, make it more pretty
|
||||
if(playerPretty.length > 0)
|
||||
{
|
||||
ffStrbufSubstrBeforeFirstC(&playerPretty, '/'); //Remove the path
|
||||
ffStrbufSubstrBeforeLastC(&playerPretty, '.'); //Remove the TLD
|
||||
}
|
||||
|
||||
//Check again for length, as we may have removed everything.
|
||||
bool playerPrettyIsCustom = playerPretty.length > 0;
|
||||
|
||||
//If we don't have subdomains, it is usually more pretty to capitalize the first letter.
|
||||
if(playerPrettyIsCustom && ffStrbufFirstIndexC(&playerPretty, '.') == playerPretty.length)
|
||||
playerPretty.chars[0] = (char) toupper(playerPretty.chars[0]);
|
||||
|
||||
if(playerPrettyIsCustom)
|
||||
ffStrbufAppendS(&playerPretty, " (");
|
||||
|
||||
ffStrbufAppend(&playerPretty, &media->player);
|
||||
|
||||
if(playerPrettyIsCustom)
|
||||
ffStrbufAppendC(&playerPretty, ')');
|
||||
|
||||
if(instance->config.playerFormat.length == 0)
|
||||
{
|
||||
ffPrintLogoAndKey(instance, FF_PLAYER_MODULE_NAME, 0, &instance->config.playerKey);
|
||||
ffStrbufPutTo(&media->playerPretty, stdout);
|
||||
ffStrbufPutTo(&playerPretty, stdout);
|
||||
}
|
||||
else
|
||||
{
|
||||
ffPrintFormatString(instance, FF_PLAYER_MODULE_NAME, 0, &instance->config.playerKey, &instance->config.playerFormat, NULL, FF_PLAYER_NUM_FORMAT_ARGS, (FFformatarg[]){
|
||||
{FF_FORMAT_ARG_TYPE_STRBUF, &media->playerPretty},
|
||||
{FF_FORMAT_ARG_TYPE_STRBUF, &playerPretty},
|
||||
{FF_FORMAT_ARG_TYPE_STRBUF, &media->player},
|
||||
{FF_FORMAT_ARG_TYPE_STRBUF, &media->busNameShort}
|
||||
});
|
||||
|
||||
+71
-5
@@ -1,7 +1,44 @@
|
||||
#include "fastfetch.h"
|
||||
|
||||
#include <ctype.h>
|
||||
|
||||
#define FF_SONG_MODULE_NAME "Song"
|
||||
#define FF_SONG_NUM_FORMAT_ARGS 4
|
||||
#define FF_SONG_NUM_FORMAT_ARGS 5
|
||||
|
||||
static bool shouldIgoreChar(char c)
|
||||
{
|
||||
return isblank(c) || c == '-' || c == '.';
|
||||
}
|
||||
|
||||
static bool artistInSongTitle(const FFstrbuf* song, const FFstrbuf* artist)
|
||||
{
|
||||
uint32_t artistIndex = 0;
|
||||
uint32_t songIndex = 0;
|
||||
|
||||
while(true)
|
||||
{
|
||||
while(shouldIgoreChar(song->chars[songIndex]))
|
||||
++songIndex;
|
||||
|
||||
while(shouldIgoreChar(artist->chars[artistIndex]))
|
||||
++artistIndex;
|
||||
|
||||
if(artist->chars[artistIndex] == '\0')
|
||||
return true;
|
||||
|
||||
if(song->chars[songIndex] == '\0')
|
||||
return false;
|
||||
|
||||
if(tolower(song->chars[songIndex]) != tolower(artist->chars[artistIndex]))
|
||||
return false;
|
||||
|
||||
++artistIndex;
|
||||
++songIndex;
|
||||
}
|
||||
|
||||
//Unreachable
|
||||
return false;
|
||||
}
|
||||
|
||||
void ffPrintSong(FFinstance* instance)
|
||||
{
|
||||
@@ -13,27 +50,56 @@ void ffPrintSong(FFinstance* instance)
|
||||
return;
|
||||
}
|
||||
|
||||
FFstrbuf songPretty;
|
||||
ffStrbufInitCopy(&songPretty, &media->song);
|
||||
const char* removeStrings[] = {
|
||||
"(Official Music Video)", "(Official Video)", "(Music Video)",
|
||||
"[Official Music Video]", "[Official Video]", "[Music Video]",
|
||||
"| Official Music Video", "| Official Video", "| Music Video",
|
||||
"[Official Audio]", "[Audio]", "(Audio)", "| Official Audio", "| Audio", "| OFFICIAL AUDIO",
|
||||
"(Lyric Video)", "(Official Lyric Video)", "(Lyrics)",
|
||||
"[Lyric Video]", "[Official Lyric Video]", "[Lyrics]",
|
||||
"| Lyric Video", "| Official Lyric Video", "| Lyrics",
|
||||
};
|
||||
ffStrbufRemoveStringsA(&songPretty, sizeof(removeStrings) / sizeof(removeStrings[0]), removeStrings);
|
||||
ffStrbufTrimRight(&songPretty, ' ');
|
||||
|
||||
if(songPretty.length == 0)
|
||||
ffStrbufAppend(&songPretty, &media->song);
|
||||
|
||||
if(instance->config.songFormat.length == 0)
|
||||
{
|
||||
//We don't expose artistPretty to the format, as it might be empty (when the think that the artist is already in the song title)
|
||||
FFstrbuf artistPretty;
|
||||
ffStrbufInitCopy(&artistPretty, &media->artist);
|
||||
ffStrbufRemoveIgnCaseEndS(&artistPretty, " - Topic");
|
||||
ffStrbufRemoveIgnCaseEndS(&artistPretty, "VEVO");
|
||||
ffStrbufTrimRight(&artistPretty, ' ');
|
||||
|
||||
if(artistInSongTitle(&songPretty, &artistPretty))
|
||||
ffStrbufClear(&artistPretty);
|
||||
|
||||
ffPrintLogoAndKey(instance, FF_SONG_MODULE_NAME, 0, &instance->config.songKey);
|
||||
|
||||
if(media->artist.length > 0)
|
||||
if(artistPretty.length > 0)
|
||||
{
|
||||
ffStrbufWriteTo(&media->artist, stdout);
|
||||
ffStrbufWriteTo(&artistPretty, stdout);
|
||||
fputs(" - ", stdout);
|
||||
}
|
||||
|
||||
if(media->album.length > 0)
|
||||
//Some sites (e.g. reddit) expose their url as album. We don't want to show that
|
||||
if(media->album.length > 0 && !ffStrbufStartsWithIgnCaseS(&media->album, "http"))
|
||||
{
|
||||
ffStrbufWriteTo(&media->album, stdout);
|
||||
fputs(" - ", stdout);
|
||||
}
|
||||
|
||||
ffStrbufPutTo(&media->song, stdout);
|
||||
ffStrbufPutTo(&songPretty, stdout);
|
||||
}
|
||||
else
|
||||
{
|
||||
ffPrintFormatString(instance, FF_SONG_MODULE_NAME, 0, &instance->config.songKey, &instance->config.songFormat, NULL, FF_SONG_NUM_FORMAT_ARGS, (FFformatarg[]){
|
||||
{FF_FORMAT_ARG_TYPE_STRBUF, &songPretty},
|
||||
{FF_FORMAT_ARG_TYPE_STRBUF, &media->song},
|
||||
{FF_FORMAT_ARG_TYPE_STRBUF, &media->artist},
|
||||
{FF_FORMAT_ARG_TYPE_STRBUF, &media->album},
|
||||
|
||||
@@ -184,7 +184,7 @@ static void printTTY(FFinstance* instance)
|
||||
FFstrbuf fontName;
|
||||
ffStrbufInit(&fontName);
|
||||
|
||||
ffParsePropFile("/etc/vconsole.conf", "Font =", &fontName);
|
||||
ffParsePropFile(FASTFETCH_TARGET_DIR_ROOT"/etc/vconsole.conf", "Font =", &fontName);
|
||||
|
||||
if(fontName.length == 0)
|
||||
{
|
||||
|
||||
+1
-1
@@ -38,7 +38,7 @@ void ffPrintTheme(FFinstance* instance)
|
||||
ffStrbufTrim(&plasmaColorPretty, ' ');
|
||||
|
||||
FF_STRBUF_CREATE(gtkPretty);
|
||||
ffGetGtkPretty(>kPretty, gtk2, gtk3, gtk4);
|
||||
ffParseGTK(>kPretty, gtk2, gtk3, gtk4);
|
||||
|
||||
if(instance->config.themeFormat.length == 0)
|
||||
{
|
||||
|
||||
+1
-1
@@ -31,7 +31,7 @@ const FFTitleResult* ffDetectTitle(FFinstance* instance)
|
||||
static inline void printTitlePart(FFinstance* instance, const FFstrbuf* content)
|
||||
{
|
||||
fputs(FASTFETCH_TEXT_MODIFIER_BOLT, stdout);
|
||||
ffPrintColor(&instance->config.color);
|
||||
ffPrintColor(&instance->config.mainColor);
|
||||
ffStrbufWriteTo(content, stdout);
|
||||
fputs(FASTFETCH_TEXT_MODIFIER_RESET, stdout);
|
||||
}
|
||||
|
||||
+34
-8
@@ -17,17 +17,43 @@ void ffPrintUptime(FFinstance* instance)
|
||||
if(days == 0 && hours == 0 && minutes == 0)
|
||||
{
|
||||
printf("%u seconds\n", seconds);
|
||||
return;
|
||||
}
|
||||
else
|
||||
|
||||
if(days > 0)
|
||||
{
|
||||
if(days > 0)
|
||||
printf("%u day%s%s, ", days, days <= 1 ? "" : "s", days < 100 ? "" : "(!)");
|
||||
if(hours > 0)
|
||||
printf("%u hour%s, ", hours, hours <= 1 ? "" : "s");
|
||||
if(minutes > 0)
|
||||
printf("%u min%s", minutes, minutes <= 1 ? "" : "s");
|
||||
putchar('\n');
|
||||
printf("%u day", days);
|
||||
|
||||
if(days > 1)
|
||||
putchar('s');
|
||||
|
||||
if(days >= 100)
|
||||
fputs("(!)", stdout);
|
||||
|
||||
if(hours > 0 || minutes > 0)
|
||||
fputs(", ", stdout);
|
||||
}
|
||||
|
||||
if(hours > 0)
|
||||
{
|
||||
printf("%u hour", hours);
|
||||
|
||||
if(hours > 1)
|
||||
putchar('s');
|
||||
|
||||
if(minutes > 0)
|
||||
fputs(", ", stdout);
|
||||
}
|
||||
|
||||
if(minutes > 0)
|
||||
{
|
||||
printf("%u min", minutes);
|
||||
|
||||
if(minutes > 1)
|
||||
putchar('s');
|
||||
}
|
||||
|
||||
putchar('\n');
|
||||
}
|
||||
else
|
||||
{
|
||||
|
||||
@@ -495,6 +495,17 @@ bool ffStrbufEndsWithS(const FFstrbuf* strbuf, const char* end)
|
||||
return true;
|
||||
}
|
||||
|
||||
uint32_t ffStrbufCountC(const FFstrbuf* strbuf, char c)
|
||||
{
|
||||
uint32_t result = 0;
|
||||
for(uint32_t i = 0; i < strbuf->length; i++)
|
||||
{
|
||||
if(strbuf->chars[i] == c)
|
||||
result++;
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
static bool testEndsWithIgnCaseS(const FFstrbuf* strbuf, const char* end, uint32_t* endLength)
|
||||
{
|
||||
*endLength = (uint32_t) strlen(end);
|
||||
|
||||
@@ -87,6 +87,8 @@ bool ffStrbufStartsWithIgnCaseS(const FFstrbuf* strbuf, const char* start);
|
||||
bool ffStrbufEndsWithC(const FFstrbuf* strbuf, char c);
|
||||
bool ffStrbufEndsWithS(const FFstrbuf* strbuf, const char* end);
|
||||
|
||||
uint32_t ffStrbufCountC(const FFstrbuf* strbuf, char c);
|
||||
|
||||
bool ffStrbufRemoveIgnCaseEndS(FFstrbuf* strbuf, const char* end);
|
||||
|
||||
void ffStrbufWriteTo(const FFstrbuf* strbuf, FILE* file);
|
||||
|
||||
+10
-6
@@ -25,12 +25,6 @@ int main(int argc, char** argv)
|
||||
ffInitInstance(&instance);
|
||||
)
|
||||
|
||||
FASTFETCH_TEST_PERFORMANCE(
|
||||
puts("Configuration");
|
||||
ffLoadLogo(&instance);
|
||||
)
|
||||
|
||||
ffStrbufSet(&instance.config.color, &instance.config.logoColors[0]);
|
||||
instance.config.showErrors = true;
|
||||
instance.config.recache = argc == 1;
|
||||
instance.config.cacheSave = false;
|
||||
@@ -41,6 +35,11 @@ int main(int argc, char** argv)
|
||||
ffStartDetectionThreads(&instance);
|
||||
)
|
||||
|
||||
FASTFETCH_TEST_PERFORMANCE(
|
||||
puts("Starting");
|
||||
ffStart(&instance);
|
||||
)
|
||||
|
||||
FASTFETCH_TEST_PERFORMANCE(ffPrintTitle(&instance))
|
||||
FASTFETCH_TEST_PERFORMANCE(ffPrintSeparator(&instance))
|
||||
FASTFETCH_TEST_PERFORMANCE(ffPrintOS(&instance))
|
||||
@@ -74,5 +73,10 @@ int main(int argc, char** argv)
|
||||
FASTFETCH_TEST_PERFORMANCE(ffPrintBreak(&instance))
|
||||
FASTFETCH_TEST_PERFORMANCE(ffPrintColors(&instance))
|
||||
|
||||
FASTFETCH_TEST_PERFORMANCE(
|
||||
puts("Finishing");
|
||||
ffFinish(&instance);
|
||||
)
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user