mirror of
https://github.com/fastfetch-cli/fastfetch.git
synced 2026-09-12 18:32:10 +02:00
Compare commits
32 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 7be0d8e716 | |||
| d964776f65 | |||
| b28170d5af | |||
| 8d531e36e7 | |||
| 0c651a697a | |||
| 29bb196e0c | |||
| 5c74223282 | |||
| 3e5ac19cd4 | |||
| 97ff3a9f86 | |||
| 2735fd6674 | |||
| f498a58b5a | |||
| fdbcdc7faa | |||
| 562c80b183 | |||
| 36cfa77ccc | |||
| c9e50c80fc | |||
| 4a482f333c | |||
| d54eea5e64 | |||
| bc2d37f398 | |||
| 9f1d38f1fa | |||
| aa21823c4e | |||
| cc906cd866 | |||
| 56c64bc643 | |||
| 6a10dab4c3 | |||
| c0371c1f32 | |||
| 83849aeefb | |||
| bed3578808 | |||
| 0c39fc4dcb | |||
| d028d57a79 | |||
| 215c6bd78f | |||
| 6a25eaa82b | |||
| 24256e8592 | |||
| cf0bdb1055 |
@@ -1,17 +0,0 @@
|
||||
root = true
|
||||
|
||||
[*]
|
||||
end_of_line = lf
|
||||
indent_style = space
|
||||
tab_width = 4
|
||||
insert_final_newline = true
|
||||
trim_trailing_whitespace = true
|
||||
|
||||
[*.{md,fflogo}]
|
||||
trim_trailing_whitespace = false
|
||||
|
||||
[*.{txt,fflogo}]
|
||||
insert_final_newline = false
|
||||
|
||||
[*.yml]
|
||||
tab_width = 2
|
||||
@@ -1,2 +0,0 @@
|
||||
*.h linguist-language=c
|
||||
*.c linguist-language=c
|
||||
@@ -1 +0,0 @@
|
||||
custom: paypal.me/linusdierheimer
|
||||
@@ -1,44 +0,0 @@
|
||||
---
|
||||
name: Bug report
|
||||
about: Create a report to help us improve
|
||||
title: ''
|
||||
labels: bug
|
||||
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
|
||||
```
|
||||
|
||||
Output of `fastfetch --load-config devinfo`:
|
||||
```
|
||||
//paste here
|
||||
```
|
||||
|
||||
Output of `fastfetch --load-config devinfo-verbose`:
|
||||
<!--
|
||||
Note that this output will contain you public IP. If it is not relevant for the issue, feel free to remove it before uploading.
|
||||
-->
|
||||
```
|
||||
//paste here
|
||||
```
|
||||
|
||||
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 too much work) for you, post the output of `fastfetch --show-errors --recache --multithreading false --disable-linewrap false`.
|
||||
-->
|
||||
|
||||
@@ -1,14 +0,0 @@
|
||||
---
|
||||
name: Feature request
|
||||
about: Suggest an idea for this project
|
||||
title: ''
|
||||
labels: enhancement
|
||||
assignees: ''
|
||||
|
||||
---
|
||||
|
||||
# Current state:
|
||||
|
||||
# Wanted state:
|
||||
|
||||
# Why the change is sensible:
|
||||
@@ -1,37 +0,0 @@
|
||||
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 rpm librpm-dev libzstd-dev libegl-dev libglx-dev libosmesa6-dev ocl-icd-opencl-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
|
||||
@@ -1,60 +0,0 @@
|
||||
name: manage pushes
|
||||
|
||||
on:
|
||||
push:
|
||||
branches: [ master ]
|
||||
|
||||
jobs:
|
||||
manage:
|
||||
name: manage push
|
||||
runs-on: ubuntu-latest
|
||||
permissions:
|
||||
contents: write
|
||||
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 rpm librpm-dev libzstd-dev libegl-dev libglx-dev libosmesa6-dev ocl-icd-opencl-dev
|
||||
|
||||
- name: Initialize CodeQL
|
||||
uses: github/codeql-action/init@v1
|
||||
|
||||
- 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 -DCMAKE_INSTALL_PREFIX=/usr .
|
||||
|
||||
- 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
|
||||
|
||||
- name: get fastfetch version
|
||||
id: get_version_fastfetch
|
||||
run: echo "::set-output name=release::$(./fastfetch --version-raw)"
|
||||
|
||||
- name: get latest release version
|
||||
id: get_version_release
|
||||
uses: pozetroninc/github-action-get-latest-release@master
|
||||
with:
|
||||
repository: ${{ github.repository }}
|
||||
|
||||
- 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-*.*
|
||||
+3
-7
@@ -1,7 +1,3 @@
|
||||
**/build/
|
||||
**/.vscode/
|
||||
**/.cache/
|
||||
**/.kdev4/
|
||||
cscope.*
|
||||
tags
|
||||
fastfetch.kdev4
|
||||
/.cache
|
||||
/.vscode
|
||||
/build*
|
||||
|
||||
@@ -1,23 +0,0 @@
|
||||
# 1.6.1
|
||||
|
||||
Fixes build on android (#205)
|
||||
|
||||
# 1.6.0
|
||||
|
||||
Features:
|
||||
* Detect QT on more DEs than just KDE Plasma. The [Plasma] category was therefore renamed to [QT]
|
||||
* Alacritty font detection
|
||||
* Load `/etc/fastfetch/config.conf` before user config
|
||||
* Disk: print one decimal point if size < 100GB
|
||||
* `--title-fqdn` option, to print fully qualified domain name instead of host name in title
|
||||
|
||||
Logos:
|
||||
* updated old NixOS logo
|
||||
|
||||
Bugfixes:
|
||||
* Correctly detect GTK on DEs that store their settings in dconf
|
||||
* Correctly detect NixOS packages
|
||||
* Mutter WM detected once again
|
||||
* Show full NixOS version in OS output
|
||||
* Don't segfault if an invalid structure is given
|
||||
* WSL doesn't output GPU anymore, as the name is always meaningless
|
||||
-551
@@ -1,551 +0,0 @@
|
||||
cmake_minimum_required(VERSION 3.12.0) # target_link_libraries with OBJECT libs
|
||||
|
||||
project(fastfetch
|
||||
VERSION 1.6.1
|
||||
LANGUAGES C
|
||||
)
|
||||
|
||||
#####################
|
||||
# Configure options #
|
||||
#####################
|
||||
|
||||
OPTION(ENABLE_LIBPCI "Enable libpci" ON)
|
||||
OPTION(ENABLE_VULKAN "Enable vulkan" ON)
|
||||
OPTION(ENABLE_WAYLAND "Enable wayland-client" ON)
|
||||
OPTION(ENABLE_XCB_RANDR "Enable xcb-randr" ON)
|
||||
OPTION(ENABLE_XCB "Enable xcb" ON)
|
||||
OPTION(ENABLE_XRANDR "Enable xrandr" ON)
|
||||
OPTION(ENABLE_X11 "Enable x11" ON)
|
||||
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_SQLITE3 "Enable sqlite3" ON)
|
||||
OPTION(ENABLE_RPM "Enable rpm" 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)
|
||||
OPTION(ENABLE_EGL "Enable egl" ON)
|
||||
OPTION(ENABLE_GLX "Enable glx" ON)
|
||||
OPTION(ENABLE_OSMESA "Enable osmesa" ON)
|
||||
OPTION(ENABLE_OPENCL "Enable opencl" ON)
|
||||
|
||||
OPTION(BUILD_TESTS "Build tests" OFF) # Also create test executables
|
||||
OPTION(SET_TWEAK "Add tweak to project version" ON) # This is set to off by github actions for release builds
|
||||
|
||||
#############################
|
||||
# Compile time dependencies #
|
||||
#############################
|
||||
|
||||
set(THREADS_PREFER_PTHREAD_FLAG ON)
|
||||
find_package(Threads REQUIRED)
|
||||
|
||||
find_package(PkgConfig REQUIRED)
|
||||
|
||||
####################
|
||||
# Compiler options #
|
||||
####################
|
||||
|
||||
if(NOT CMAKE_BUILD_TYPE)
|
||||
set(CMAKE_BUILD_TYPE Release)
|
||||
endif()
|
||||
|
||||
set(CMAKE_C_STANDARD 11)
|
||||
set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -Wall -Wextra -Wconversion")
|
||||
|
||||
include(CheckIPOSupported)
|
||||
check_ipo_supported(RESULT IPO_SUPPORTED)
|
||||
if(IPO_SUPPORTED)
|
||||
set(CMAKE_INTERPROCEDURAL_OPTIMIZATION TRUE)
|
||||
endif()
|
||||
|
||||
#######################
|
||||
# Target 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()
|
||||
|
||||
#################
|
||||
# Tweak 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()
|
||||
|
||||
#############
|
||||
# Text data #
|
||||
#############
|
||||
|
||||
function(fastfetch_load_text FILENAME OUTVAR)
|
||||
file(READ "${FILENAME}" TEMP)
|
||||
string(REPLACE "\n" "\\n" TEMP "${TEMP}")
|
||||
string(REPLACE "\"" "\\\"" TEMP "${TEMP}")
|
||||
string(REPLACE "$\\" "" TEMP "${TEMP}")
|
||||
set("${OUTVAR}" "${TEMP}" PARENT_SCOPE)
|
||||
endfunction(fastfetch_load_text)
|
||||
|
||||
fastfetch_load_text(src/data/structure.txt DATATEXT_STRUCTURE)
|
||||
fastfetch_load_text(src/data/config_system.txt DATATEXT_CONFIG_SYSTEM)
|
||||
fastfetch_load_text(src/data/config_user.txt DATATEXT_CONFIG_USER)
|
||||
fastfetch_load_text(src/data/modules.txt DATATEXT_MODULES)
|
||||
fastfetch_load_text(src/data/help.txt DATATEXT_HELP)
|
||||
fastfetch_load_text(src/data/help_color.txt DATATEXT_HELP_COLOR)
|
||||
fastfetch_load_text(src/data/help_format.txt DATATEXT_HELP_FORMAT)
|
||||
fastfetch_load_text(src/data/help_config.txt DATATEXT_HELP_CONFIG)
|
||||
|
||||
######################
|
||||
# Configure config.h #
|
||||
######################
|
||||
|
||||
configure_file(src/fastfetch_config.h.in fastfetch_config.h)
|
||||
|
||||
#######################
|
||||
# libfastfetch target #
|
||||
#######################
|
||||
|
||||
add_library(libfastfetch OBJECT
|
||||
src/util/FFstrbuf.c
|
||||
src/util/FFlist.c
|
||||
src/util/FFvaluestore.c
|
||||
src/common/init.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/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/qt.c
|
||||
src/detection/gtk.c
|
||||
src/detection/terminalShell.c
|
||||
src/detection/vulkan.c
|
||||
src/detection/media.c
|
||||
src/detection/datetime.c
|
||||
src/detection/temps.c
|
||||
src/detection/title.c
|
||||
src/detection/displayserver/displayServer.c
|
||||
src/detection/displayserver/wayland.c
|
||||
src/detection/displayserver/xcb.c
|
||||
src/detection/displayserver/xlib.c
|
||||
src/detection/displayserver/wmde.c
|
||||
src/modules/break.c
|
||||
src/modules/custom.c
|
||||
src/modules/title.c
|
||||
src/modules/separator.c
|
||||
src/modules/os.c
|
||||
src/modules/host.c
|
||||
src/modules/kernel.c
|
||||
src/modules/uptime.c
|
||||
src/modules/processes.c
|
||||
src/modules/packages.c
|
||||
src/modules/shell.c
|
||||
src/modules/resolution.c
|
||||
src/modules/de.c
|
||||
src/modules/wm.c
|
||||
src/modules/wmtheme.c
|
||||
src/modules/theme.c
|
||||
src/modules/icons.c
|
||||
src/modules/font.c
|
||||
src/modules/cursor.c
|
||||
src/modules/terminal.c
|
||||
src/modules/terminalfont.c
|
||||
src/modules/cpu.c
|
||||
src/modules/cpuUsage.c
|
||||
src/modules/gpu.c
|
||||
src/modules/memory.c
|
||||
src/modules/disk.c
|
||||
src/modules/battery.c
|
||||
src/modules/locale.c
|
||||
src/modules/vulkan.c
|
||||
src/modules/localip.c
|
||||
src/modules/publicip.c
|
||||
src/modules/player.c
|
||||
src/modules/song.c
|
||||
src/modules/datetime.c
|
||||
src/modules/date.c
|
||||
src/modules/time.c
|
||||
src/modules/colors.c
|
||||
src/modules/opengl.c
|
||||
src/modules/opencl.c
|
||||
)
|
||||
|
||||
if(ENABLE_LIBPCI)
|
||||
pkg_check_modules(LIBPCI libpci)
|
||||
if(LIBPCI_FOUND)
|
||||
target_compile_definitions(libfastfetch PRIVATE FF_HAVE_LIBPCI=1)
|
||||
else()
|
||||
message(WARNING "Package libpci not found. Building without support.")
|
||||
endif()
|
||||
endif()
|
||||
|
||||
if(ENABLE_VULKAN)
|
||||
pkg_check_modules(VULKAN vulkan)
|
||||
if(VULKAN_FOUND)
|
||||
target_compile_definitions(libfastfetch PRIVATE FF_HAVE_VULKAN=1)
|
||||
else()
|
||||
message(WARNING "Package vulkan not found. Building without support.")
|
||||
endif()
|
||||
endif()
|
||||
|
||||
if(ENABLE_WAYLAND)
|
||||
pkg_check_modules (WAYLAND wayland-client)
|
||||
if(WAYLAND_FOUND)
|
||||
target_compile_definitions(libfastfetch PRIVATE FF_HAVE_WAYLAND=1)
|
||||
else()
|
||||
message(WARNING "Package wayland-client not found. Building without support.")
|
||||
endif()
|
||||
endif()
|
||||
|
||||
if(ENABLE_XCB_RANDR)
|
||||
pkg_check_modules(XCB_RANDR xcb-randr)
|
||||
if(XCB_RANDR_FOUND)
|
||||
target_compile_definitions(libfastfetch PRIVATE FF_HAVE_XCB_RANDR=1)
|
||||
else()
|
||||
message(WARNING "Package xcb-randr not found. Building without support.")
|
||||
endif()
|
||||
endif()
|
||||
|
||||
if(ENABLE_XCB)
|
||||
pkg_check_modules (XCB xcb)
|
||||
if(XCB_FOUND)
|
||||
target_compile_definitions(libfastfetch PRIVATE FF_HAVE_XCB=1)
|
||||
else()
|
||||
message(WARNING "Package xcb not found. Building without support.")
|
||||
endif()
|
||||
endif()
|
||||
|
||||
if(ENABLE_XRANDR)
|
||||
pkg_check_modules(XRANDR xrandr)
|
||||
if(XRANDR_FOUND)
|
||||
target_compile_definitions(libfastfetch PRIVATE FF_HAVE_XRANDR=1)
|
||||
else()
|
||||
message(WARNING "Package xrandr not found. Building without support.")
|
||||
endif()
|
||||
endif()
|
||||
|
||||
if(ENABLE_X11)
|
||||
pkg_check_modules(X11 x11)
|
||||
if(X11_FOUND)
|
||||
target_compile_definitions(libfastfetch PRIVATE FF_HAVE_X11=1)
|
||||
else()
|
||||
message(WARNING "Package x11 not found. Building without support.")
|
||||
endif()
|
||||
endif()
|
||||
|
||||
if(ENABLE_GIO)
|
||||
pkg_check_modules(GIO gio-2.0)
|
||||
if(GIO_FOUND)
|
||||
target_compile_definitions(libfastfetch PRIVATE FF_HAVE_GIO=1)
|
||||
else()
|
||||
message(WARNING "Package gio-2.0 not found. Building without support.")
|
||||
endif()
|
||||
endif()
|
||||
|
||||
if(ENABLE_DCONF)
|
||||
pkg_check_modules(DCONF dconf)
|
||||
if(DCONF_FOUND)
|
||||
target_compile_definitions(libfastfetch PRIVATE FF_HAVE_DCONF=1)
|
||||
else()
|
||||
message(WARNING "Package dconf not found. Building without support.")
|
||||
endif()
|
||||
endif()
|
||||
|
||||
if(ENABLE_DBUS)
|
||||
pkg_check_modules(DBUS dbus-1)
|
||||
if(DBUS_FOUND)
|
||||
target_compile_definitions(libfastfetch PRIVATE FF_HAVE_DBUS=1)
|
||||
else()
|
||||
message(WARNING "Package dbus-1 not found. Building without support.")
|
||||
endif()
|
||||
endif()
|
||||
|
||||
if(ENABLE_XFCONF)
|
||||
pkg_check_modules(XFCONF libxfconf-0)
|
||||
if(XFCONF_FOUND)
|
||||
target_compile_definitions(libfastfetch PRIVATE FF_HAVE_XFCONF=1)
|
||||
else()
|
||||
message(WARNING "Package libxfconf-0 not found. Building without support.")
|
||||
endif()
|
||||
endif()
|
||||
|
||||
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_RPM)
|
||||
pkg_check_modules(RPM rpm)
|
||||
if(RPM_FOUND)
|
||||
target_compile_definitions(libfastfetch PRIVATE FF_HAVE_RPM=1)
|
||||
else()
|
||||
message(WARNING "Package rpm 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)
|
||||
if(IMAGEMAGICK6_FOUND OR IMAGEMAGICK7_FOUND)
|
||||
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()
|
||||
else()
|
||||
message(WARNING "Chafa not enabled, because neither ImageMagick6 nor ImageMagick7 were found. Building without support.")
|
||||
endif()
|
||||
endif()
|
||||
|
||||
if(ENABLE_EGL)
|
||||
pkg_check_modules(EGL egl)
|
||||
if(EGL_FOUND)
|
||||
target_compile_definitions(libfastfetch PRIVATE FF_HAVE_EGL=1)
|
||||
else()
|
||||
message(WARNING "Package egl not found. Building without support.")
|
||||
endif()
|
||||
endif()
|
||||
|
||||
if(ENABLE_GLX)
|
||||
pkg_check_modules(GLX glx)
|
||||
if(GLX_FOUND)
|
||||
target_compile_definitions(libfastfetch PRIVATE FF_HAVE_GLX=1)
|
||||
else()
|
||||
message(WARNING "Package glx not found. Building without support.")
|
||||
endif()
|
||||
endif()
|
||||
|
||||
if(ENABLE_OSMESA)
|
||||
pkg_check_modules(OSMESA osmesa)
|
||||
if(OSMESA_FOUND)
|
||||
target_compile_definitions(libfastfetch PRIVATE FF_HAVE_OSMESA=1)
|
||||
else()
|
||||
message(WARNING "Package osmesa not found. Building without support.")
|
||||
endif()
|
||||
endif()
|
||||
|
||||
if(ENABLE_OPENCL)
|
||||
pkg_check_modules(OPENCL OpenCL)
|
||||
if(OPENCL_FOUND)
|
||||
target_compile_definitions(libfastfetch PRIVATE FF_HAVE_OPENCL=1)
|
||||
else()
|
||||
message(WARNING "Package opencl not found. Building without support.")
|
||||
endif()
|
||||
endif()
|
||||
|
||||
target_include_directories(libfastfetch
|
||||
PUBLIC ${PROJECT_BINARY_DIR}
|
||||
PUBLIC ${PROJECT_SOURCE_DIR}/src
|
||||
|
||||
PRIVATE ${LIBPCI_INCLUDE_DIRS}
|
||||
PRIVATE ${VULKAN_INCLUDE_DIRS}
|
||||
PRIVATE ${WAYLAND_INCLUDE_DIRS}
|
||||
PRIVATE ${XCB_RANDR_INCLUDE_DIRS}
|
||||
PRIVATE ${XCB_INCLUDE_DIRS}
|
||||
PRIVATE ${XRANDR_INCLUDE_DIRS}
|
||||
PRIVATE ${X11_INCLUDE_DIRS}
|
||||
PRIVATE ${GIO_INCLUDE_DIRS}
|
||||
PRIVATE ${DCONF_INCLUDE_DIRS}
|
||||
PRIVATE ${DBUS_INCLUDE_DIRS}
|
||||
PRIVATE ${XFCONF_INCLUDE_DIRS}
|
||||
PRIVATE ${SQLITE3_INCLUDE_DIRS}
|
||||
PRIVATE ${RPM_INCLUDE_DIRS}
|
||||
PRIVATE ${IMAGEMAGICK7_INCLUDE_DIRS}
|
||||
PRIVATE ${IMAGEMAGICK6_INCLUDE_DIRS}
|
||||
PRIVATE ${ZLIB_INCLUDE_DIRS}
|
||||
PRIVATE ${CHAFA_INCLUDE_DIRS}
|
||||
PRIVATE ${VULKAN_INCLUDE_DIRS}
|
||||
PRIVATE ${EGL_INCLUDE_DIRS}
|
||||
PRIVATE ${GLX_INCLUDE_DIRS}
|
||||
PRIVATE ${OSMESA_INCLUDE_DIRS}
|
||||
PRIVATE ${OPENCL_INCLUDE_DIRS}
|
||||
)
|
||||
|
||||
target_link_libraries(libfastfetch
|
||||
PRIVATE ${CMAKE_DL_LIBS}
|
||||
PRIVATE Threads::Threads
|
||||
)
|
||||
|
||||
######################
|
||||
# Executable targets #
|
||||
######################
|
||||
|
||||
add_executable(fastfetch
|
||||
src/fastfetch.c
|
||||
)
|
||||
target_link_libraries(fastfetch
|
||||
PRIVATE libfastfetch
|
||||
)
|
||||
|
||||
add_executable(flashfetch
|
||||
src/flashfetch.c
|
||||
)
|
||||
target_link_libraries(flashfetch
|
||||
PRIVATE libfastfetch
|
||||
)
|
||||
|
||||
###################
|
||||
# Testing targets #
|
||||
###################
|
||||
|
||||
if (BUILD_TESTS)
|
||||
add_executable(fastfetch-test-strbuf
|
||||
tests/strbuf.c
|
||||
)
|
||||
target_link_libraries(fastfetch-test-strbuf
|
||||
PRIVATE libfastfetch
|
||||
)
|
||||
|
||||
enable_testing()
|
||||
add_test(NAME test-strbuf COMMAND fastfetch-test-strbuf)
|
||||
endif()
|
||||
|
||||
##################
|
||||
# install target #
|
||||
##################
|
||||
|
||||
if(NOT CMAKE_INSTALL_SYSCONFDIR)
|
||||
set(CMAKE_INSTALL_SYSCONFDIR "/etc")
|
||||
endif()
|
||||
|
||||
include(GNUInstallDirs)
|
||||
|
||||
install(
|
||||
TARGETS fastfetch flashfetch
|
||||
DESTINATION ${CMAKE_INSTALL_BINDIR}
|
||||
)
|
||||
|
||||
install(
|
||||
FILES src/data/config_system.txt
|
||||
DESTINATION ${CMAKE_INSTALL_SYSCONFDIR}/${CMAKE_PROJECT_NAME}
|
||||
RENAME "config.conf"
|
||||
)
|
||||
|
||||
install(
|
||||
FILES completions/bash
|
||||
DESTINATION ${CMAKE_INSTALL_DATAROOTDIR}/bash-completion/completions
|
||||
RENAME ${CMAKE_PROJECT_NAME}
|
||||
)
|
||||
|
||||
install(
|
||||
DIRECTORY presets
|
||||
DESTINATION ${CMAKE_INSTALL_DATAROOTDIR}/${CMAKE_PROJECT_NAME}
|
||||
)
|
||||
|
||||
install(
|
||||
FILES ./LICENSE
|
||||
DESTINATION ${CMAKE_INSTALL_DATAROOTDIR}/licenses/${CMAKE_PROJECT_NAME}
|
||||
)
|
||||
|
||||
##################
|
||||
# package target #
|
||||
##################
|
||||
|
||||
set(CPACK_GENERATOR "DEB;RPM;TGZ;ZIP")
|
||||
set(CPACK_PACKAGING_INSTALL_PREFIX "/usr")
|
||||
set(CPACK_SET_DESTDIR ON)
|
||||
|
||||
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_RPM_PACKAGE_LICENSE "MIT")
|
||||
|
||||
include(CPack)
|
||||
@@ -1,36 +0,0 @@
|
||||
# Development help
|
||||
|
||||
This file is not an official documentation!
|
||||
Here i just add things that are easy to forget.
|
||||
|
||||
## Features i am planning to implement
|
||||
|
||||
### Specific
|
||||
|
||||
- [ ] Split the project in different libs / executables, all linking static by default:
|
||||
- `libffdetect`: contains all the detection stuff. To be used by anything that needs system information
|
||||
- `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
|
||||
- [ ] Better OS output for all possible combinations of /etc/os-release variables.
|
||||
- [ ] Find wayland compositor by looking at \${XDG_RUNTIME_DIR}/${WAYLAND_DISPLAY:-wayland-0}
|
||||
- [ ] Make LocalIP module more configurable
|
||||
- [ ] Automatic migrate old config files to newer versions
|
||||
- [ ] ZSH completions
|
||||
- [ ] Fish completions
|
||||
- [ ] 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
|
||||
|
||||
### General
|
||||
- More presets
|
||||
- More logos
|
||||
- More package managers
|
||||
- More DE detections
|
||||
- More WM theme detections
|
||||
- More terminal font detections
|
||||
- Performance optimizations
|
||||
- More module specific options
|
||||
- Better error handling, especially the messages shown when using --show-errors
|
||||
- Using package managers libraries for package count detection instead own. E.g. using libalpm instead of counting files in /var/lib/pacman/local
|
||||
- Better documentation (especially default config file and the various --help options. Colored output?)
|
||||
- General feature parity with neofetch, to demonstrate supremity of c
|
||||
@@ -1,21 +0,0 @@
|
||||
MIT License
|
||||
|
||||
Copyright (c) 2021 Linus Dierheimer
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
in the Software without restriction, including without limitation the rights
|
||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
copies of the Software, and to permit persons to whom the Software is
|
||||
furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in all
|
||||
copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
SOFTWARE.
|
||||
@@ -1,110 +1,29 @@
|
||||
# fastfetch
|
||||
# Fastfetch Benchmark Data
|
||||
|
||||
fastfetch is a [neofetch](https://github.com/dylanaraps/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.
|
||||
|
||||
The output on my personal machine with default configurations looks like this:
|
||||
<img src="screenshots/example.png" alt="example output" width="100%"/>
|
||||
This branch stores continuous performance benchmark data for Fastfetch on Linux, macOS, and Windows.
|
||||
|
||||
## Customization
|
||||
The data is generated and updated automatically by GitHub Actions. Please do not edit generated benchmark files manually.
|
||||
|
||||
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 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.
|
||||
## Benchmark Dashboard
|
||||
|
||||
## Dependencies
|
||||
<https://fastfetch-cli.github.io/fastfetch/dev/bench/>
|
||||
|
||||
Fastfetch dynamically loads needed libraries if they are available. Therefore its only hard dependencies are `libc` (any implementation of the c standard library), `libdl` and `libpthread`. They are all shipped with [`glibc`](https://www.gnu.org/software/libc/), which is already installed on most linux distributions, so you probably don't have to worry about it.
|
||||
## Metrics
|
||||
|
||||
The following libraries are used if present at runtime:
|
||||
* [`libpci`](https://github.com/pciutils/pciutils): GPU output.
|
||||
* [`libvulkan`](https://www.vulkan.org/): Vulkan module & fallback for GPU output.
|
||||
* [`libxcb-randr`](https://xcb.freedesktop.org/),
|
||||
[`libXrandr`](https://gitlab.freedesktop.org/xorg/lib/libxrandr),
|
||||
[`libxcb`](https://xcb.freedesktop.org/),
|
||||
[`libX11`](https://gitlab.freedesktop.org/xorg/lib/libx11): At least one of them sould be present in X11 sessions for better resolution detection and faster WM detection. The `*randr` ones provide multi monitor support The `libxcb*` ones usually have better performance.
|
||||
* [`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.
|
||||
* [`libEGL`](https://www.khronos.org/registry/EGL/),
|
||||
[`libGLX`](https://dri.freedesktop.org/wiki/GLX/),
|
||||
[`libOSMesa`](https://docs.mesa3d.org/osmesa.html): At least one of them is needed by the OpenGL module for gl context creation.
|
||||
* [`libOpenCL`](https://www.khronos.org/opencl/): OpenCL module
|
||||
* [`libXFConf`](https://gitlab.xfce.org/xfce/xfconf): Needed for XFWM theme and XFCE Terminal font.
|
||||
* [`libsqlite3`](https://www.sqlite.org/index.html): Needed for rpm package count.
|
||||
* [`librpm`](http://rpm.org/): Slower fallback for rpm package count. Needed on openSUSE.
|
||||
The benchmark data includes:
|
||||
|
||||
## Support status
|
||||
All categories not listed here should work without needing a specific implementation.
|
||||
- Execution time for each Fastfetch module, measured in milliseconds;
|
||||
- Total Fastfetch process execution time, measured in milliseconds.
|
||||
|
||||
##### Available Modules
|
||||
```
|
||||
Title, Separator, OS, Host, Kernel, Uptime, Processes, Packages, Shell, Resolution, DE, WM, WMTheme, Theme, Icons, Font, Cursor, Terminal, Terminal Font, CPU, CPUUsage, GPU, Memory, Disk, Battery, Player, Song, Vulkan, OpenGL, OpenCL, LocalIP, PublicIP, DateTime, Date, Time, Locale, Colors, Break, Custom
|
||||
```
|
||||
All metrics use the “smaller is better” comparison mode.
|
||||
|
||||
##### Logos
|
||||
```
|
||||
Alpine, Android, Arch, Arco, Artix, Bedrock, 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.
|
||||
## Update Workflow
|
||||
|
||||
##### Package managers
|
||||
```
|
||||
Pacman, dpkg, rpm, emerge, xbps, nix, Flatpak, Snap
|
||||
```
|
||||
Benchmark data is generated from the `dev` branch CI workflow and updated after all platform build jobs have completed.
|
||||
|
||||
##### WM themes
|
||||
```
|
||||
KWin, Mutter, Muffin, Marco, XFWM, Openbox (LXDE, LXQT & without DE)
|
||||
```
|
||||
Related workflow files:
|
||||
|
||||
##### DE versions
|
||||
```
|
||||
KDE Plasma, Gnome, Cinnamon, Mate, XFCE4, LXQt
|
||||
```
|
||||
- `.github/workflows/build-benchmark.yml`
|
||||
- `.github/workflows/ci.yml`
|
||||
|
||||
##### Terminal fonts
|
||||
```
|
||||
konsole, gnome-terminal-server, tilix, xfce4-terminal, alacritty, lxterminal, TTY
|
||||
```
|
||||
|
||||
## Building
|
||||
|
||||
fastfetch uses [`cmake`](https://cmake.org/) and [`pkg-config`](https://www.freedesktop.org/wiki/Software/pkg-config/) for building. The simplest steps to build the fastfetch and flashfetch binaries are:
|
||||
```bash
|
||||
mkdir -p build
|
||||
cd build
|
||||
cmake ..
|
||||
cmake --build . --target fastfetch --target flashfetch
|
||||
```
|
||||
|
||||
If pkg-config fails to find the headers for a library listed in [dependencies](#dependencies), fastfetch will simply build without support for that specific feature. This means, it won't look for it at runtime and just act like it isn't available.
|
||||
|
||||
## Packaging
|
||||
|
||||
### Repositories
|
||||
|
||||
[](https://repology.org/project/fastfetch/versions)
|
||||
|
||||
### Manual
|
||||
|
||||
* DEB / RPM package: `cmake --build . --target package`
|
||||
* Install directly: `cmake --install . --prefix /usr/local`
|
||||
|
||||
## FAQ
|
||||
|
||||
Q: Why do you need a very performant version of neofetch?
|
||||
> I like putting neofetch in my ~/.bashrc to have a system overwiew whenever i use the terminal, but the slow speed annoyed me, so i created this. Also neofetch didn't output everything correctly (e.g Font is displayed as "[Plasma], Noto Sans, 10 [GTK2/3]") and writing my own tool gave me the possibility to fine tune it to run perfectly on at least my configuration.
|
||||
|
||||
Q: It does not display [*] correctly for me, what can i do?
|
||||
> This is most likely because your system is not implemented (yet). At the moment i am focusing more on making the core app better, than adding more configurations. Feel free to open a pull request if you want to add support for your configuration
|
||||
Commit information and dates are recorded automatically by the Continuous Benchmark Action.
|
||||
|
||||
@@ -1,319 +0,0 @@
|
||||
#!/usr/bin/env bash
|
||||
|
||||
__fastfetch_complete_help()
|
||||
{
|
||||
local __ff_helps=(
|
||||
"color"
|
||||
"format"
|
||||
"load-config"
|
||||
"os-format"
|
||||
"host-format"
|
||||
"kernel-format"
|
||||
"packages-format"
|
||||
"shell-format"
|
||||
"resolution-format"
|
||||
"de-format"
|
||||
"wm-format"
|
||||
"theme-format"
|
||||
"icons-format"
|
||||
"font-format"
|
||||
"terminal-format"
|
||||
"terminal-font-format"
|
||||
"cpu-format"
|
||||
"cpu-usage-format"
|
||||
"gpu-format"
|
||||
"memory-format"
|
||||
"disk-format"
|
||||
"battery-format"
|
||||
"locale-format"
|
||||
"local-ip-format"
|
||||
"public-ip-format"
|
||||
"datetime-format"
|
||||
"date-format"
|
||||
"time-format"
|
||||
)
|
||||
COMPREPLY=($(compgen -W "${__ff_helps[*]}" -- "$CURRENT_WORD"))
|
||||
}
|
||||
|
||||
__fastfetch_complete_bool()
|
||||
{
|
||||
COMPREPLY=($(compgen -W "true false" -- "$CURRENT_WORD"))
|
||||
}
|
||||
|
||||
__fastfetch_complete_string()
|
||||
{
|
||||
if [[ $CURRENT_WORD != "" ]]; then
|
||||
COMPREPLY=("$CURRENT_WORD")
|
||||
fi
|
||||
}
|
||||
|
||||
__fastfetch_complete_path()
|
||||
{
|
||||
COMPREPLY=($(compgen -A file -- "$CURRENT_WORD"))
|
||||
}
|
||||
|
||||
__fastfetch_complete_logo()
|
||||
{
|
||||
COMPREPLY=($(compgen -W "$(fastfetch --list-logos-autocompletion)" -- "$CURRENT_WORD"))
|
||||
}
|
||||
|
||||
__fastfetch_complete_option()
|
||||
{
|
||||
local FF_OPTIONS_ALL=(
|
||||
"${FF_OPTIONS_BOOL[@]}"
|
||||
"${FF_OPTIONS_STRING[@]}"
|
||||
"${FF_OPTIONS_PATH[@]}"
|
||||
"${FF_OPTIONS_LOGO[@]}"
|
||||
)
|
||||
|
||||
if [[ $WORD_COUND -lt 3 ]]; then
|
||||
FF_OPTIONS_ALL+=(
|
||||
"${FF_OPTIONS_SINGLE[@]}"
|
||||
"${FF_OPTIONS_HELP[@]}"
|
||||
)
|
||||
fi
|
||||
|
||||
for ff_word in ${COMP_WORDS[@]}; do
|
||||
if [[ $ff_word == $CURRENT_WORD ]]; then
|
||||
break
|
||||
fi
|
||||
|
||||
FF_OPTIONS_ALL=("${FF_OPTIONS_ALL[@]/$ff_word}")
|
||||
done
|
||||
|
||||
COMPREPLY=($(compgen -W "${FF_OPTIONS_ALL[*]}" -- "$CURRENT_WORD"))
|
||||
}
|
||||
|
||||
__fastfetch_previous_matches()
|
||||
{
|
||||
for ff_option in "$@"; do
|
||||
if [[ $ff_option == "$PREVIOUS_WORD" ]]; then
|
||||
return 0
|
||||
fi
|
||||
done
|
||||
return 1
|
||||
}
|
||||
|
||||
__fastfetch_completion()
|
||||
{
|
||||
local CURRENT_WORD="${COMP_WORDS[$COMP_CWORD]}"
|
||||
local PREVIOUS_WORD="${COMP_WORDS[$COMP_CWORD - 1]}"
|
||||
local WORD_COUND="${#COMP_WORDS[@]}"
|
||||
|
||||
local FF_OPTIONS_SINGLE=(
|
||||
"-v"
|
||||
"--version"
|
||||
"--list-logos"
|
||||
"--list-modules"
|
||||
"--list-presets"
|
||||
"--list-features"
|
||||
"--print-logos"
|
||||
"--print-config-system"
|
||||
"--print-config-user"
|
||||
"--print-structure"
|
||||
)
|
||||
|
||||
local FF_OPTIONS_HELP=(
|
||||
"-h"
|
||||
"--help"
|
||||
)
|
||||
|
||||
local FF_OPTIONS_BOOL=(
|
||||
"-r"
|
||||
"--recache"
|
||||
"--nocache"
|
||||
"--show-errors"
|
||||
"--logo-print-remaining"
|
||||
"--multithreading"
|
||||
"--allow-slow-operations"
|
||||
"--disable-linewrap"
|
||||
"--hide-cursor"
|
||||
"--localip-show-ipv4"
|
||||
"--localip-show-ipv6"
|
||||
"--localip-show-loop"
|
||||
"--escape-bedrock"
|
||||
"--pipe"
|
||||
"--title-fqdn"
|
||||
)
|
||||
|
||||
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"
|
||||
"--separator"
|
||||
"-s"
|
||||
"--structure"
|
||||
"--set"
|
||||
"--gl"
|
||||
"--os-format"
|
||||
"--os-key"
|
||||
"--host-format"
|
||||
"--host-key"
|
||||
"--kernel-format"
|
||||
"--kernel-key"
|
||||
"--uptime-format"
|
||||
"--uptime-key"
|
||||
"--processes-format"
|
||||
"--processes-key"
|
||||
"--packages-format"
|
||||
"--packages-key"
|
||||
"--shell-format"
|
||||
"--shell-key"
|
||||
"--resolution-format"
|
||||
"--resolution-key"
|
||||
"--de-format"
|
||||
"--de-key"
|
||||
"--wm-format"
|
||||
"--wm-key"
|
||||
"--wm-theme-format"
|
||||
"--wm-theme-key"
|
||||
"--theme-format"
|
||||
"--theme-key"
|
||||
"--icons-format"
|
||||
"--icons-key"
|
||||
"--font-format"
|
||||
"--font-key"
|
||||
"--cursor-format"
|
||||
"--cursor-key"
|
||||
"--terminal-format"
|
||||
"--terminal-key"
|
||||
"--terminal-font-format"
|
||||
"--terminal-font-key"
|
||||
"--cpu-format"
|
||||
"--cpu-key"
|
||||
"--cpu-usage-format"
|
||||
"--cpu-usage-key"
|
||||
"--gpu-format"
|
||||
"--gpu-key"
|
||||
"--memory-format"
|
||||
"--memory-key"
|
||||
"--disk-format"
|
||||
"--disk-key"
|
||||
"--battery-format"
|
||||
"--battery-key"
|
||||
"--locale-format"
|
||||
"--locale-key"
|
||||
"--disk-folders"
|
||||
"--disk-key"
|
||||
"--local-ip-format"
|
||||
"--local-ip-key"
|
||||
"--public-ip-format"
|
||||
"--public-ip-key"
|
||||
"--player-format"
|
||||
"--player-key"
|
||||
"--song-format"
|
||||
"--song-key"
|
||||
"--datetime-key"
|
||||
"--datetime-format"
|
||||
"--date-format"
|
||||
"--date-key"
|
||||
"--time-format"
|
||||
"--time-key"
|
||||
"--vulkan-key"
|
||||
"--vulkan-format"
|
||||
"--opengl-key"
|
||||
"--opengl-format"
|
||||
"--opencl-key"
|
||||
"--opencl-format"
|
||||
"--os-error"
|
||||
"--host-error"
|
||||
"--kernel-error"
|
||||
"--uptime-error"
|
||||
"--processes-error"
|
||||
"--packages-error"
|
||||
"--shell-error"
|
||||
"--resolution-error"
|
||||
"--de-error"
|
||||
"--wm-error"
|
||||
"--wm-theme-error"
|
||||
"--theme-error"
|
||||
"--icons-error"
|
||||
"--font-error"
|
||||
"--cursor-error"
|
||||
"--terminal-error"
|
||||
"--terminal-font-error"
|
||||
"--cpu-error"
|
||||
"--cpu-usage-error"
|
||||
"--gpu-error"
|
||||
"--memory-error"
|
||||
"--disk-error"
|
||||
"--battery-error"
|
||||
"--locale-error"
|
||||
"--disk-error"
|
||||
"--local-ip-error"
|
||||
"--public-ip-error"
|
||||
"--player-error"
|
||||
"--song-error"
|
||||
"--datetime-error"
|
||||
"--date-error"
|
||||
"--time-error"
|
||||
"--vulkan-error"
|
||||
"--opengl-error"
|
||||
"--opencl-error"
|
||||
)
|
||||
|
||||
local FF_OPTIONS_PATH=(
|
||||
"--os-file"
|
||||
"--lib-PCI"
|
||||
"--lib-vulkan"
|
||||
"--lib-wayland"
|
||||
"--lib-xcb-randr"
|
||||
"--lib-xcb"
|
||||
"--lib-Xrandr"
|
||||
"--lib-X11"
|
||||
"--lib-gio"
|
||||
"--lib-DConf"
|
||||
"--lib-DBus"
|
||||
"--lib-XFConf"
|
||||
"--lib-sqlite3"
|
||||
"--lib-rpm"
|
||||
"--lib-imagemagick"
|
||||
"--lib-z"
|
||||
"--lib-chafa"
|
||||
"--lib-egl"
|
||||
"--lib-glx"
|
||||
"--lib-osmesa"
|
||||
"--lib-opencl"
|
||||
"--battery-dir"
|
||||
"--load-config"
|
||||
)
|
||||
|
||||
local FF_OPTIONS_LOGO=(
|
||||
"-l"
|
||||
"--logo"
|
||||
)
|
||||
|
||||
if __fastfetch_previous_matches "${FF_OPTIONS_SINGLE[@]}"; then
|
||||
return
|
||||
elif [[ $WORD_COUND -gt 3 && ( ${COMP_WORDS[$COMP_CWORD - 2]} == "--help" || ${COMP_WORDS[$COMP_CWORD - 2]} == "-h" ) ]]; then
|
||||
return
|
||||
elif [[ $CURRENT_WORD == "-"* ]]; then
|
||||
__fastfetch_complete_option
|
||||
elif __fastfetch_previous_matches "${FF_OPTIONS_HELP[@]}"; then
|
||||
__fastfetch_complete_help
|
||||
elif __fastfetch_previous_matches "${FF_OPTIONS_BOOL[@]}"; then
|
||||
__fastfetch_complete_bool
|
||||
elif __fastfetch_previous_matches "${FF_OPTIONS_STRING[@]}"; then
|
||||
__fastfetch_complete_string
|
||||
elif __fastfetch_previous_matches "${FF_OPTIONS_PATH[@]}"; then
|
||||
__fastfetch_complete_path
|
||||
elif __fastfetch_previous_matches "${FF_OPTIONS_LOGO[@]}"; then
|
||||
__fastfetch_complete_logo
|
||||
else
|
||||
__fastfetch_complete_option
|
||||
fi
|
||||
}
|
||||
|
||||
complete -F __fastfetch_completion fastfetch
|
||||
+69572
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,405 @@
|
||||
<!doctype html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="utf-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1">
|
||||
<title>Fastfetch · Benchmark</title>
|
||||
<style>
|
||||
:root {
|
||||
color-scheme: light;
|
||||
--page: #f5f7fb;
|
||||
--card: #fff;
|
||||
--text: #172033;
|
||||
--muted: #64748b;
|
||||
--border: #dbe3ef;
|
||||
--accent: #2563eb;
|
||||
--accent-soft: #dbeafe;
|
||||
--chart-text: #334155;
|
||||
--chart-grid: #cbd5e1;
|
||||
}
|
||||
:root[data-theme="dark"] {
|
||||
color-scheme: dark;
|
||||
--page: #0f172a; --card: #172033; --text: #e5edf8; --muted: #9aabc1; --border: #334155; --accent-soft: #1e3a8a;
|
||||
--chart-text: #f1f5f9;
|
||||
--chart-grid: #475569;
|
||||
}
|
||||
* { box-sizing: border-box; }
|
||||
body { max-width: 1440px; margin: 0 auto; padding: 2.5rem 1.25rem 4rem; background: var(--page); color: var(--text); font: 16px/1.5 system-ui, -apple-system, sans-serif; }
|
||||
header { margin-bottom: 1.75rem; }
|
||||
.header-row { display: flex; align-items: center; justify-content: space-between; gap: 1rem; }
|
||||
h1 { margin: 0; font-size: clamp(2rem, 4vw, 3rem); letter-spacing: -.04em; }
|
||||
h2 { margin: 0; font-size: 1.25rem; }
|
||||
p { color: var(--muted); }
|
||||
.intro { margin: .6rem 0 1rem; }
|
||||
.links a { color: var(--accent); font-weight: 600; text-decoration: none; }
|
||||
.links a:hover { text-decoration: underline; }
|
||||
.theme-toggle, .fullscreen { border: 1px solid var(--border); border-radius: .55rem; padding: .5rem .8rem; background: var(--card); color: var(--text); cursor: pointer; font: inherit; font-weight: 600; }
|
||||
.theme-toggle:hover, .fullscreen:hover { border-color: var(--accent); color: var(--accent); }
|
||||
.tabs { display: flex; flex-wrap: wrap; gap: .6rem; margin: 1.5rem 0; padding: .35rem; border: 1px solid var(--border); border-radius: .8rem; background: var(--card); }
|
||||
.tab { border: 0; border-radius: .55rem; padding: .65rem 1.15rem; background: transparent; color: var(--muted); cursor: pointer; font: inherit; font-weight: 700; }
|
||||
.tab:hover, .tab[aria-selected="true"] { background: var(--accent); color: #fff; }
|
||||
.tab-panel[hidden] { display: none; }
|
||||
.platform-title { display: flex; align-items: center; gap: .65rem; margin: 1.5rem 0 1rem; }
|
||||
.platform-title::before { content: ''; width: .35rem; height: 1.4rem; border-radius: 1rem; background: var(--accent); }
|
||||
.charts { display: flex; flex-direction: column; gap: 1.25rem; }
|
||||
.chart { min-width: 0; padding: 1.25rem; border: 1px solid var(--border); border-radius: 1rem; background: var(--card); box-shadow: 0 8px 28px #0f172a0c; }
|
||||
.chart:fullscreen { overflow: auto; padding: 2rem; background: var(--page); }
|
||||
.chart:fullscreen .canvas-wrap { height: calc(100vh - 13rem); }
|
||||
.chart-header { display: flex; align-items: baseline; justify-content: space-between; gap: 1rem; margin-bottom: .75rem; }
|
||||
.chart-header span { color: var(--muted); font-size: .85rem; }
|
||||
.canvas-wrap { position: relative; height: 360px; }
|
||||
.chart-actions { display: flex; align-items: center; gap: .75rem; }
|
||||
.legend-actions { display: flex; flex-wrap: wrap; gap: .45rem; margin-top: 1rem; }
|
||||
.legend-action { border: 1px solid var(--border); border-radius: .45rem; padding: .35rem .6rem; background: var(--card); color: var(--muted); cursor: pointer; font: inherit; font-size: .8rem; }
|
||||
.legend-action:hover { border-color: var(--accent); color: var(--accent); }
|
||||
.legend { display: flex; flex-wrap: wrap; gap: .35rem .9rem; margin-top: 1rem; }
|
||||
.legend-item { display: inline-flex; align-items: center; gap: .4rem; border: 0; padding: .15rem 0; background: transparent; color: var(--text); cursor: pointer; font: inherit; font-size: .85rem; }
|
||||
.legend-item.is-hidden { color: var(--muted); }
|
||||
.legend-swatch { width: .75rem; height: .75rem; border-radius: 50%; }
|
||||
.legend-item.is-hidden .legend-swatch { opacity: .35; }
|
||||
.chart-tooltip { position: fixed; z-index: 1000; pointer-events: none; width: min(90vw, 900px); max-height: calc(100vh - 24px); overflow: hidden; padding: .7rem .85rem; border: 1px solid var(--border); border-radius: .6rem; background: var(--card); color: var(--text); box-shadow: 0 10px 30px #0f172a33; font-size: .8rem; opacity: 0; transition: opacity .1s ease; }
|
||||
.chart-tooltip-title { margin-bottom: .35rem; font-weight: 700; white-space: normal; overflow-wrap: anywhere; }
|
||||
.chart-tooltip-body { max-height: calc(100vh - 76px); overflow: auto; columns: 220px auto; column-gap: 1.5rem; }
|
||||
.chart-tooltip-row { display: flex; align-items: center; gap: .45rem; break-inside: avoid; white-space: nowrap; }
|
||||
.chart-tooltip-swatch { width: .55rem; height: .55rem; border-radius: 50%; flex: 0 0 auto; }
|
||||
.empty { padding: 2rem 0; color: var(--muted); }
|
||||
</style>
|
||||
<script src="https://cdn.jsdelivr.net/npm/chart.js/dist/chart.umd.min.js"></script>
|
||||
<script src="data.js"></script>
|
||||
</head>
|
||||
<body>
|
||||
<header>
|
||||
<div class="header-row">
|
||||
<h1>Fastfetch Benchmark</h1>
|
||||
<button id="theme-toggle" class="theme-toggle" type="button"></button>
|
||||
</div>
|
||||
<p class="intro">Performance history grouped by CPU family. Each chart contains all modules and total process time for one operating system and architecture. Module curves are available from the legend and hidden by default.</p>
|
||||
<p class="links"><a href="data.js">Download raw benchmark data</a></p>
|
||||
</header>
|
||||
<nav id="tabs" class="tabs" aria-label="CPU architecture"></nav>
|
||||
<main id="panels"></main>
|
||||
<script>
|
||||
(() => {
|
||||
const benchmarkData = window.BENCHMARK_DATA || {};
|
||||
const entries = Object.values(benchmarkData.entries || {}).flat();
|
||||
const groups = new Map();
|
||||
|
||||
function architectureFamily(arch) {
|
||||
const normalized = arch.toLowerCase();
|
||||
if (/^(amd64|x86_64|x86|i[3-6]86)$/.test(normalized)) return 'Intel';
|
||||
if (/^(aarch64|arm64|armv[5-8].*|arm)$/.test(normalized)) return 'ARM';
|
||||
return 'Other';
|
||||
}
|
||||
|
||||
function parseName(name) {
|
||||
let match = name.match(/^fastfetch (linux|macos|windows) ([^ ]+) (module .+|total process time)$/i);
|
||||
if (match) {
|
||||
return { platform: match[1].toLowerCase(), arch: match[2], metric: match[3] };
|
||||
}
|
||||
|
||||
// Keep older macOS records visible while new records use the platform field.
|
||||
match = name.match(/^fastfetch ([^ ]+) (module .+|total process time)$/i);
|
||||
return match ? { platform: 'macos', arch: match[1], metric: match[2] } : null;
|
||||
}
|
||||
|
||||
function entryMetadata(entry) {
|
||||
const raw = entry.commit && entry.commit.timestamp || entry.date;
|
||||
const date = new Date(raw);
|
||||
return {
|
||||
date: Number.isNaN(date.valueOf()) ? String(entry.date || '') : date.toISOString(),
|
||||
sha: entry.commit && entry.commit.id ? entry.commit.id : 'unknown'
|
||||
};
|
||||
}
|
||||
|
||||
for (const entry of entries) {
|
||||
const metricsByGroup = new Map();
|
||||
for (const bench of entry.benches || []) {
|
||||
const parsed = parseName(bench.name);
|
||||
if (!parsed) continue;
|
||||
const key = `${parsed.platform} ${parsed.arch}`;
|
||||
if (!metricsByGroup.has(key)) metricsByGroup.set(key, new Map());
|
||||
metricsByGroup.get(key).set(parsed.metric, bench.value);
|
||||
}
|
||||
|
||||
for (const [key, metrics] of metricsByGroup) {
|
||||
if (!groups.has(key)) groups.set(key, []);
|
||||
groups.get(key).push({ ...entryMetadata(entry), metrics });
|
||||
}
|
||||
}
|
||||
|
||||
const colors = [
|
||||
'#2563eb', '#dc2626', '#16a34a', '#9333ea', '#ea580c', '#0891b2',
|
||||
'#db2777', '#65a30d', '#4f46e5', '#ca8a04', '#0f766e', '#be123c'
|
||||
];
|
||||
const tabs = document.getElementById('tabs');
|
||||
const panels = document.getElementById('panels');
|
||||
const root = document.documentElement;
|
||||
const themeToggle = document.getElementById('theme-toggle');
|
||||
const savedTheme = localStorage.getItem('fastfetch-benchmark-theme');
|
||||
let theme = savedTheme || (matchMedia('(prefers-color-scheme: dark)').matches ? 'dark' : 'light');
|
||||
const chartsByInstance = [];
|
||||
|
||||
function chartColors() {
|
||||
const styles = getComputedStyle(root);
|
||||
return {
|
||||
text: styles.getPropertyValue('--chart-text').trim(),
|
||||
grid: styles.getPropertyValue('--chart-grid').trim()
|
||||
};
|
||||
}
|
||||
|
||||
function externalTooltipHandler(context) {
|
||||
const { chart, tooltip } = context;
|
||||
let tooltipElement = chart.canvas.parentNode.querySelector('.chart-tooltip');
|
||||
if (!tooltipElement) {
|
||||
tooltipElement = document.createElement('div');
|
||||
tooltipElement.className = 'chart-tooltip';
|
||||
chart.canvas.parentNode.appendChild(tooltipElement);
|
||||
}
|
||||
|
||||
if (tooltip.opacity === 0) {
|
||||
tooltipElement.style.opacity = '0';
|
||||
return;
|
||||
}
|
||||
|
||||
tooltipElement.replaceChildren();
|
||||
const title = document.createElement('div');
|
||||
title.className = 'chart-tooltip-title';
|
||||
const row = chart._benchmarkRows[tooltip.dataPoints[0].dataIndex];
|
||||
title.textContent = `${new Date(row.date).toLocaleString()} · ${row.sha}`;
|
||||
tooltipElement.appendChild(title);
|
||||
const bodyElement = document.createElement('div');
|
||||
bodyElement.className = 'chart-tooltip-body';
|
||||
for (const body of [...tooltip.dataPoints].sort((a, b) => b.parsed.y - a.parsed.y)) {
|
||||
const item = document.createElement('div');
|
||||
item.className = 'chart-tooltip-row';
|
||||
const swatch = document.createElement('span');
|
||||
swatch.className = 'chart-tooltip-swatch';
|
||||
swatch.style.background = body.dataset.borderColor;
|
||||
const value = document.createElement('span');
|
||||
value.textContent = `${body.dataset.label}: ${body.formattedValue} ms`;
|
||||
item.append(swatch, value);
|
||||
bodyElement.appendChild(item);
|
||||
}
|
||||
tooltipElement.appendChild(bodyElement);
|
||||
|
||||
const canvasRect = chart.canvas.getBoundingClientRect();
|
||||
const margin = 12;
|
||||
let left = canvasRect.left + tooltip.caretX + margin;
|
||||
let top = canvasRect.top + tooltip.caretY + margin;
|
||||
tooltipElement.style.left = `${left}px`;
|
||||
tooltipElement.style.top = `${top}px`;
|
||||
tooltipElement.style.opacity = '0';
|
||||
const tooltipRect = tooltipElement.getBoundingClientRect();
|
||||
if (left + tooltipRect.width > window.innerWidth - margin) left = canvasRect.left + tooltip.caretX - tooltipRect.width - margin;
|
||||
if (top + tooltipRect.height > window.innerHeight - margin) top = canvasRect.top + tooltip.caretY - tooltipRect.height - margin;
|
||||
tooltipElement.style.left = `${Math.max(margin, left)}px`;
|
||||
tooltipElement.style.top = `${Math.max(margin, top)}px`;
|
||||
tooltipElement.style.opacity = '1';
|
||||
}
|
||||
|
||||
function updateTheme() {
|
||||
root.dataset.theme = theme;
|
||||
themeToggle.textContent = theme === 'dark' ? '☀ Light mode' : '☾ Dark mode';
|
||||
themeToggle.setAttribute('aria-label', `Switch to ${theme === 'dark' ? 'light' : 'dark'} mode`);
|
||||
const colors = chartColors();
|
||||
Chart.defaults.color = colors.text;
|
||||
Chart.defaults.borderColor = colors.grid;
|
||||
for (const chart of chartsByInstance) {
|
||||
chart.options.scales.x.title.color = colors.text;
|
||||
chart.options.scales.x.ticks.color = colors.text;
|
||||
chart.options.scales.x.grid.color = colors.grid;
|
||||
chart.options.scales.y.title.color = colors.text;
|
||||
chart.options.scales.y.ticks.color = colors.text;
|
||||
chart.options.scales.y.grid.color = colors.grid;
|
||||
chart.update();
|
||||
}
|
||||
}
|
||||
themeToggle.addEventListener('click', () => {
|
||||
theme = theme === 'dark' ? 'light' : 'dark';
|
||||
localStorage.setItem('fastfetch-benchmark-theme', theme);
|
||||
updateTheme();
|
||||
});
|
||||
updateTheme();
|
||||
const families = new Map();
|
||||
|
||||
for (const [key, rows] of groups) {
|
||||
const arch = key.slice(key.lastIndexOf(' ') + 1);
|
||||
const family = architectureFamily(arch);
|
||||
if (!families.has(family)) families.set(family, []);
|
||||
families.get(family).push({ key, rows });
|
||||
}
|
||||
|
||||
const familyOrder = ['Intel', 'ARM', 'Other'];
|
||||
const availableFamilies = familyOrder.filter(family => families.has(family));
|
||||
|
||||
for (const [index, family] of availableFamilies.entries()) {
|
||||
const tab = document.createElement('button');
|
||||
const panelId = `panel-${family.toLowerCase()}`;
|
||||
tab.className = 'tab';
|
||||
tab.type = 'button';
|
||||
tab.id = `tab-${family.toLowerCase()}`;
|
||||
tab.setAttribute('role', 'tab');
|
||||
tab.textContent = family;
|
||||
tab.setAttribute('aria-controls', panelId);
|
||||
tab.setAttribute('aria-selected', index === 0 ? 'true' : 'false');
|
||||
tabs.appendChild(tab);
|
||||
|
||||
const panel = document.createElement('section');
|
||||
panel.className = 'tab-panel';
|
||||
panel.id = panelId;
|
||||
panel.setAttribute('role', 'tabpanel');
|
||||
panel.hidden = index !== 0;
|
||||
panel.setAttribute('aria-labelledby', tab.id);
|
||||
const title = document.createElement('h2');
|
||||
title.className = 'platform-title';
|
||||
title.textContent = `${family} processors`;
|
||||
panel.appendChild(title);
|
||||
const chartGrid = document.createElement('div');
|
||||
chartGrid.className = 'charts';
|
||||
panel.appendChild(chartGrid);
|
||||
panels.appendChild(panel);
|
||||
|
||||
tab.addEventListener('click', () => {
|
||||
for (const otherTab of tabs.children) otherTab.setAttribute('aria-selected', otherTab === tab ? 'true' : 'false');
|
||||
for (const otherPanel of panels.children) otherPanel.hidden = otherPanel !== panel;
|
||||
});
|
||||
|
||||
for (const { key, rows } of families.get(family).sort((a, b) => a.key.localeCompare(b.key))) {
|
||||
rows.sort((a, b) => a.date.localeCompare(b.date));
|
||||
const metrics = [...new Set(rows.flatMap(row => [...row.metrics.keys()]))].sort((a, b) => {
|
||||
if (a === 'total process time') return -1;
|
||||
if (b === 'total process time') return 1;
|
||||
return a.localeCompare(b);
|
||||
});
|
||||
const section = document.createElement('article');
|
||||
section.className = 'chart';
|
||||
const chartHeader = document.createElement('div');
|
||||
chartHeader.className = 'chart-header';
|
||||
const chartTitle = document.createElement('h2');
|
||||
chartTitle.textContent = key;
|
||||
const chartActions = document.createElement('div');
|
||||
chartActions.className = 'chart-actions';
|
||||
const chartHint = document.createElement('span');
|
||||
chartHint.textContent = `${rows.length} run${rows.length === 1 ? '' : 's'}`;
|
||||
const fullscreen = document.createElement('button');
|
||||
fullscreen.className = 'fullscreen';
|
||||
fullscreen.type = 'button';
|
||||
fullscreen.textContent = '⛶ Fullscreen';
|
||||
fullscreen.addEventListener('click', () => {
|
||||
if (document.fullscreenElement) document.exitFullscreen();
|
||||
else section.requestFullscreen();
|
||||
});
|
||||
chartActions.append(chartHint, fullscreen);
|
||||
chartHeader.append(chartTitle, chartActions);
|
||||
const canvasWrap = document.createElement('div');
|
||||
canvasWrap.className = 'canvas-wrap';
|
||||
const canvas = document.createElement('canvas');
|
||||
canvasWrap.appendChild(canvas);
|
||||
section.append(chartHeader, canvasWrap);
|
||||
chartGrid.appendChild(section);
|
||||
|
||||
const colorsForChart = chartColors();
|
||||
const chart = new Chart(canvas, {
|
||||
type: 'line',
|
||||
data: {
|
||||
labels: rows.map(row => row.sha.slice(0, 7)),
|
||||
datasets: metrics.map((metric, index) => ({
|
||||
label: metric,
|
||||
data: rows.map(row => row.metrics.has(metric) ? row.metrics.get(metric) : null),
|
||||
borderColor: colors[index % colors.length],
|
||||
backgroundColor: colors[index % colors.length],
|
||||
pointRadius: 3,
|
||||
tension: 0.2,
|
||||
spanGaps: true,
|
||||
hidden: metric !== 'total process time'
|
||||
}))
|
||||
},
|
||||
options: {
|
||||
responsive: true,
|
||||
maintainAspectRatio: false,
|
||||
interaction: { mode: 'index', intersect: false },
|
||||
plugins: {
|
||||
legend: { display: false },
|
||||
tooltip: {
|
||||
enabled: false,
|
||||
external: externalTooltipHandler,
|
||||
callbacks: {
|
||||
title: tooltipItems => {
|
||||
const row = rows[tooltipItems[0].dataIndex];
|
||||
return `${row.date} · ${row.sha}`;
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
scales: {
|
||||
x: { title: { display: true, text: 'Date', color: colorsForChart.text }, ticks: { color: colorsForChart.text }, grid: { color: colorsForChart.grid } },
|
||||
y: { beginAtZero: true, title: { display: true, text: 'Milliseconds', color: colorsForChart.text }, ticks: { color: colorsForChart.text }, grid: { color: colorsForChart.grid } }
|
||||
}
|
||||
}
|
||||
});
|
||||
chart._benchmarkRows = rows;
|
||||
chartsByInstance.push(chart);
|
||||
const legendActions = document.createElement('div');
|
||||
legendActions.className = 'legend-actions';
|
||||
const legend = document.createElement('div');
|
||||
legend.className = 'legend';
|
||||
const legendItems = [];
|
||||
|
||||
function refreshLegend() {
|
||||
legendItems.forEach((item, metricIndex) => {
|
||||
item.classList.toggle('is-hidden', !chart.isDatasetVisible(metricIndex));
|
||||
});
|
||||
}
|
||||
|
||||
function setAllLegends(visible) {
|
||||
metrics.forEach((metric, metricIndex) => chart.setDatasetVisibility(metricIndex, visible));
|
||||
refreshLegend();
|
||||
chart.update();
|
||||
}
|
||||
|
||||
function addLegendAction(label, action) {
|
||||
const button = document.createElement('button');
|
||||
button.className = 'legend-action';
|
||||
button.type = 'button';
|
||||
button.textContent = label;
|
||||
button.addEventListener('click', action);
|
||||
legendActions.appendChild(button);
|
||||
}
|
||||
|
||||
addLegendAction('Show all', () => setAllLegends(true));
|
||||
addLegendAction('Toggle all', () => {
|
||||
metrics.forEach((metric, metricIndex) => chart.setDatasetVisibility(metricIndex, !chart.isDatasetVisible(metricIndex)));
|
||||
refreshLegend();
|
||||
chart.update();
|
||||
});
|
||||
addLegendAction('Reset', () => {
|
||||
metrics.forEach((metric, metricIndex) => chart.setDatasetVisibility(metricIndex, metric === 'total process time'));
|
||||
refreshLegend();
|
||||
chart.update();
|
||||
});
|
||||
|
||||
metrics.forEach((metric, metricIndex) => {
|
||||
const item = document.createElement('button');
|
||||
item.className = `legend-item${chart.isDatasetVisible(metricIndex) ? '' : ' is-hidden'}`;
|
||||
item.type = 'button';
|
||||
item.innerHTML = `<span class="legend-swatch" style="background:${colors[metricIndex % colors.length]}"></span>${metric}`;
|
||||
item.addEventListener('click', () => {
|
||||
chart.setDatasetVisibility(metricIndex, !chart.isDatasetVisible(metricIndex));
|
||||
item.classList.toggle('is-hidden', !chart.isDatasetVisible(metricIndex));
|
||||
chart.update();
|
||||
});
|
||||
legendItems.push(item);
|
||||
legend.appendChild(item);
|
||||
});
|
||||
section.append(legendActions, legend);
|
||||
}
|
||||
}
|
||||
|
||||
if (!groups.size) {
|
||||
tabs.hidden = true;
|
||||
panels.innerHTML = '<p class="empty">No benchmark data available.</p>';
|
||||
}
|
||||
})();
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
||||
@@ -1 +0,0 @@
|
||||
--structure Title:Separator:OS:Host:Kernel:Uptime:Processes:Packages:Shell:Resolution:DE:WM:WMTheme:Theme:Icons:Font:Cursor:Terminal:TerminalFont:CPU:GPU:Memory:Disk:Battery:Player:Song:PublicIP:LocalIP:DateTime:Locale:Vulkan:OpenGL:OpenCL:Break:Colors
|
||||
@@ -1,3 +0,0 @@
|
||||
--logo arch
|
||||
--os-format "Arch Linux ({12})"
|
||||
--packages-format "{} (pacman)"
|
||||
@@ -1,4 +0,0 @@
|
||||
--disable-linewrap false
|
||||
--multithreading false
|
||||
--show-errors true
|
||||
--recache
|
||||
@@ -1,2 +0,0 @@
|
||||
--load-config verbose
|
||||
--load-config devinfo
|
||||
@@ -1,4 +0,0 @@
|
||||
--structure Host:Resolution:CPU:GPU:Memory:Disk:Battery
|
||||
--memory-format "{?2}{2}MiB{?}"
|
||||
--disk-format "{?2}{2}GB{?}"
|
||||
--battery-format "{?1}{1} {?}{?2}{2} {?}{?3}({3}){?}"
|
||||
@@ -1,10 +0,0 @@
|
||||
--structure "Title:Separator:OS:Host:Kernel:Uptime:Packages:Shell:Resolution:DE:WM:WMTheme:Theme:Icons:Terminal:TerminalFont:CPU:GPU:Memory:Break:Colors"
|
||||
--host-format "{/2}{-}{/}{2}{?3} {3}{?}"
|
||||
--os-format "{3} {12}"
|
||||
--resolution-format "{}x{}"
|
||||
--de-format "{2} {3}"
|
||||
--theme-format "{?1}{1}{?3} {3}{?} [Plasma], {?}{7}"
|
||||
--terminal-font-format "{/2}{-}{/}{2}{?3} {3}{?}"
|
||||
--memory-format "{/1}{-}{/}{/2}{-}{/}{}MiB / {}MiB"
|
||||
--gpu-key "GPU"
|
||||
--resolution-key "Resolution"
|
||||
@@ -1,5 +0,0 @@
|
||||
--structure Title:Separator:OS:Host:Kernel:Uptime:Battery:Break:Packages:Shell:Resolution:Terminal:Break:CPU:GPU:Memory:Break:Colors
|
||||
--host-format "{/2}{-}{/}{2}{?3} {3}{?}"
|
||||
--battery-format "{/4}{-}{/}{4}%{?5} [{5}]{?}"
|
||||
--resolution-key "Resolution"
|
||||
--gpu-key "GPU"
|
||||
@@ -1 +0,0 @@
|
||||
--structure OS:Kernel:Shell:DE:WM:WMTheme:Theme:Icons:Font:Cursor:Terminal:TerminalFont
|
||||
@@ -1,31 +0,0 @@
|
||||
--load-config all
|
||||
|
||||
--os-format "System: {}; Name: {}; Pretty name: {}; ID: {}; ID like: {}; Variant: {}; Variant ID: {}; Version: {}; Version ID: {}; Version codename: {}; Build ID: {}; Architecture: {}"
|
||||
--host-format "Family: "product_family: {}; product_name: {}; product_version: {}; product_sku: {}; bios_date: {}; bios_release: {}; bios_vendor: {}; bios_version: {}; board_name: {}; board_vendor: {}; board_version: {}; chassis_type: {}; chassis_vendor: {}; chassis_version: {}; sys_vendor: {}"
|
||||
--kernel-format "Sysname: {}; Release: {}; Version: {}"
|
||||
--uptime-format "Days: {}; Hours: {}; Minutes: {}; Seconds: {}"
|
||||
--processes-format "Count: {}"
|
||||
--packages-format "All: {}; pacman: {}; pacman branch: {}; dpkg: {}; rpm: {}; emerge: {}; xbps: {}; flatpak: {}; snap: {}"
|
||||
--shell-format "Process name: {}; Process path: {}; Process exe: {}; Process version: {}; User path: {}; User exe: {}; User version: {}"
|
||||
--resolution-format "Width: {}; Height: {}; Refresh rate: {}"
|
||||
--de-format "Process name: {}; Pretty name: {}; Version: {}"
|
||||
--wm-format "Process name: {}; Pretty name: {}; Protocol: {}"
|
||||
--wm-theme-format "Name: {}"
|
||||
--theme-format "Plasma: {}; Plasma colors: {}; Plasma colors pretty: {}; GTK2: {}; GTK3: {}; GTK4: {}; GTK: {}"
|
||||
--icons-format "Plasma: {}; GTK2: {}; GTK3: {}; GTK4: {}; GTK: {}"
|
||||
--font-format "Plasma raw: {}; Plasma name: {}; Plasma size: {}; Plasma styles: {}; Plasma pretty: {}; GTK2 raw: {}; GTK2 name: {}; GTK2 size: {}; GTK2 styles: {}; GTK2 pretty: {}; GTK3 raw: {}; GTK3 name: {}; GTK3 size: {}; GTK3 styles: {}; GTK3 pretty: {}; GTK4 raw: {}; GTK4 name: {}; GTK4 size: {}; GTK4 styles: {}; GTK4 pretty: {}; GTK: {}"
|
||||
--cursor-format "Theme: {}; Size: {}"
|
||||
--terminal-format "Process: {}; Path: {}; Exe: {}"
|
||||
--terminal-font-format "Raw: {}; Name: {}; Size: {}; Styles: {}; Pretty: {}"
|
||||
--cpu-format "Name: {}; Pretty: {}; Vendor: {}; Logical online: {}; Logical configured: {}; Physical: {}; Cores: {}; temperature: {}; bios: {}; scaling max: {}; scaling min: {}; info max: {}; info min: {}; cpuinfo: {}; frequency: {}"
|
||||
--cpu-usage-format "Percentage: {}"
|
||||
--gpu-format "Vendor: {}; Vendor pretty: {}; Name: {}; Name pretty: {}; Driver: {}; Temperature: {}"
|
||||
--memory-format "Used: {}; Total: {}; Percentage: {}"
|
||||
--disk-format "Used: {}; Total: {}; Files: {}; Percentage: {}"
|
||||
--battery-format "Manufactor: {}; Model: {}; Technology: {}; Capacaty: {}; Status: {}"
|
||||
--player-format "Name: {}"
|
||||
--song-format "Song: {}; Artist: {}; Album: {}"
|
||||
--datetime-format "year: {}; yearShort: {}; month: {}; monthPretty: {}; monthName: {}; monthNameShort: {}; weekNumber: {}; weekday: {}; weekdayShort: {}; dayInYear: {}; dayInMonth: {}; dayInWeek: {}; hour: {}; hourPretty: {}; hour12: {}; hour12Pretty: {}; minute: {}; minutePretty: {}; second: {}; secondPretty: {}"
|
||||
--vulkan-format "driver: {}; Api Version: {}; Conformance Version: {}"
|
||||
--opengl-format "version: {}; renderer: {}; vendor: {}"
|
||||
--opencl-format "version: {}; device: {}; vendor: {}"
|
||||
@@ -1,11 +0,0 @@
|
||||
#!/bin/env sh
|
||||
|
||||
# make the script exit if any of the commands fail,
|
||||
# making fastfetch not run if the build failed.
|
||||
set -e
|
||||
|
||||
mkdir -p build/
|
||||
cd build/
|
||||
cmake ..
|
||||
cmake --build . --target fastfetch -j$(nproc)
|
||||
./fastfetch "$@"
|
||||
Binary file not shown.
|
Before Width: | Height: | Size: 208 KiB |
Binary file not shown.
|
Before Width: | Height: | Size: 39 KiB |
@@ -1,229 +0,0 @@
|
||||
#include "fastfetch.h"
|
||||
#include "common/caching.h"
|
||||
#include "common/io.h"
|
||||
#include "common/printing.h"
|
||||
|
||||
#include <stdlib.h>
|
||||
|
||||
#define FF_CACHE_VALUE_EXTENSION "ffcv"
|
||||
#define FF_CACHE_SPLIT_EXTENSION "ffcs"
|
||||
|
||||
#define FF_CACHE_EXTENSION_DATA "ffcd"
|
||||
|
||||
static void getCacheFilePath(const 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);
|
||||
ffAppendFileBuffer(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);
|
||||
ffWriteFileBuffer(path.chars, content);
|
||||
ffStrbufDestroy(&path);
|
||||
}
|
||||
|
||||
void ffCacheValidate(FFinstance* instance)
|
||||
{
|
||||
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 FFModuleArgs* moduleArgs)
|
||||
{
|
||||
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, &moduleArgs->key);
|
||||
puts(content.chars + startIndex);
|
||||
startIndex = nullByteIndex + 1;
|
||||
++moduleCounter;
|
||||
}
|
||||
|
||||
ffStrbufDestroy(&content);
|
||||
|
||||
return moduleCounter > 1;
|
||||
}
|
||||
|
||||
static bool printCachedFormat(FFinstance* instance, const char* moduleName, const FFModuleArgs* moduleArgs, 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;
|
||||
ffPrintFormat(instance, moduleName, moduleIndex, moduleArgs, numArgs, arguments);
|
||||
++moduleCounter;
|
||||
argumentCounter = 0;
|
||||
}
|
||||
|
||||
startIndex = nullByteIndex + 1;
|
||||
}
|
||||
|
||||
free(arguments);
|
||||
ffStrbufDestroy(&content);
|
||||
|
||||
return moduleCounter > 1;
|
||||
}
|
||||
|
||||
bool ffPrintFromCache(FFinstance* instance, const char* moduleName, const FFModuleArgs* moduleArgs, uint32_t numArgs)
|
||||
{
|
||||
if(instance->config.recache)
|
||||
return false;
|
||||
|
||||
if(moduleArgs->outputFormat.length == 0)
|
||||
return printCachedValue(instance, moduleName, moduleArgs);
|
||||
else
|
||||
return printCachedFormat(instance, moduleName, moduleArgs, numArgs);
|
||||
}
|
||||
|
||||
void ffPrintAndAppendToCache(FFinstance* instance, const char* moduleName, uint8_t moduleIndex, const FFModuleArgs* moduleArgs, FFcache* cache, const FFstrbuf* value, uint32_t numArgs, const FFformatarg* arguments)
|
||||
{
|
||||
if(moduleArgs->outputFormat.length == 0)
|
||||
{
|
||||
ffPrintLogoAndKey(instance, moduleName, moduleIndex, &moduleArgs->key);
|
||||
ffStrbufPutTo(value, stdout);
|
||||
}
|
||||
else
|
||||
{
|
||||
ffPrintFormat(instance, moduleName, moduleIndex, moduleArgs, 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 FFModuleArgs* moduleArgs, const FFstrbuf* value, uint32_t numArgs, const FFformatarg* arguments)
|
||||
{
|
||||
FFcache cache;
|
||||
ffCacheOpenWrite(instance, moduleName, &cache);
|
||||
ffPrintAndAppendToCache(instance, moduleName, 0, moduleArgs, &cache, value, numArgs, arguments);
|
||||
ffCacheClose(&cache);
|
||||
}
|
||||
|
||||
void ffCachingWriteData(const FFinstance* instance, const char* moduleName, size_t dataSize, const void* data)
|
||||
{
|
||||
FFstrbuf path;
|
||||
ffStrbufInitA(&path, 128);
|
||||
getCacheFilePath(instance, moduleName, FF_CACHE_EXTENSION_DATA, &path);
|
||||
ffWriteFileData(path.chars, dataSize, data);
|
||||
ffStrbufDestroy(&path);
|
||||
}
|
||||
|
||||
bool ffCachingReadData(const FFinstance* instance, const char* moduleName, size_t dataSize, void* data)
|
||||
{
|
||||
if(instance->config.recache)
|
||||
return false;
|
||||
|
||||
FFstrbuf path;
|
||||
ffStrbufInitA(&path, 128);
|
||||
getCacheFilePath(instance, moduleName, FF_CACHE_EXTENSION_DATA, &path);
|
||||
ssize_t result = ffReadFileData(path.chars, dataSize, data);
|
||||
ffStrbufDestroy(&path);
|
||||
return result > 0 && (size_t) result == dataSize;
|
||||
}
|
||||
@@ -1,27 +0,0 @@
|
||||
#pragma once
|
||||
|
||||
#ifndef FF_INCLUDED_common_caching
|
||||
#define FF_INCLUDED_common_caching
|
||||
|
||||
#include "fastfetch.h"
|
||||
#include "common/format.h"
|
||||
|
||||
typedef struct FFcache
|
||||
{
|
||||
FILE* value;
|
||||
FILE* split;
|
||||
} FFcache;
|
||||
|
||||
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 FFModuleArgs* moduleArgs, uint32_t numArgs);
|
||||
void ffPrintAndAppendToCache(FFinstance* instance, const char* moduleName, uint8_t moduleIndex, const FFModuleArgs* moduleArgs, FFcache* cache, const FFstrbuf* value, uint32_t numArgs, const FFformatarg* arguments);
|
||||
void ffPrintAndWriteToCache(FFinstance* instance, const char* moduleName, const FFModuleArgs* moduleArgs, const FFstrbuf* value, uint32_t numArgs, const FFformatarg* arguments);
|
||||
|
||||
void ffCachingWriteData(const FFinstance* instance, const char* moduleName, size_t dataSize, const void* data);
|
||||
bool ffCachingReadData(const FFinstance* instance, const char* moduleName, size_t dataSize, void* data);
|
||||
|
||||
#endif
|
||||
@@ -1,216 +0,0 @@
|
||||
#include "fastfetch.h"
|
||||
#include "common/font.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 ffFontInitValues(FFfont* font, const char* name, const char* size)
|
||||
{
|
||||
fontInit(font);
|
||||
|
||||
ffStrbufAppendS(&font->name, name);
|
||||
ffStrbufAppendS(&font->size, size);
|
||||
|
||||
fontInitPretty(font);
|
||||
}
|
||||
|
||||
void ffFontInitCopy(FFfont* font, const char* name)
|
||||
{
|
||||
ffFontInitValues(font, name, NULL);
|
||||
}
|
||||
|
||||
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);
|
||||
}
|
||||
@@ -1,23 +0,0 @@
|
||||
#pragma once
|
||||
|
||||
#ifndef FF_INCLUDED_common_font
|
||||
#define FF_INCLUDED_common_font
|
||||
|
||||
#include "util/FFstrbuf.h"
|
||||
#include "util/FFlist.h"
|
||||
|
||||
typedef struct FFfont
|
||||
{
|
||||
FFstrbuf pretty;
|
||||
FFstrbuf name;
|
||||
FFstrbuf size;
|
||||
FFlist styles;
|
||||
} FFfont;
|
||||
|
||||
void ffFontInitQt(FFfont* font, const char* data);
|
||||
void ffFontInitPango(FFfont* font, const char* data);
|
||||
void ffFontInitCopy(FFfont* font, const char* name);
|
||||
void ffFontInitValues(FFfont* font, const char* name, const char* size);
|
||||
void ffFontDestroy(FFfont* font);
|
||||
|
||||
#endif
|
||||
@@ -1,258 +0,0 @@
|
||||
#include "fastfetch.h"
|
||||
#include "common/format.h"
|
||||
|
||||
void ffFormatAppendFormatArg(FFstrbuf* buffer, const FFformatarg* formatarg)
|
||||
{
|
||||
if(formatarg->type == FF_FORMAT_ARG_TYPE_INT)
|
||||
ffStrbufAppendF(buffer, "%i", *(int*)formatarg->value);
|
||||
else if(formatarg->type == FF_FORMAT_ARG_TYPE_UINT)
|
||||
ffStrbufAppendF(buffer, "%u", *(uint32_t*)formatarg->value);
|
||||
else if(formatarg->type == FF_FORMAT_ARG_TYPE_UINT16)
|
||||
ffStrbufAppendF(buffer, "%hu", *(uint16_t*)formatarg->value);
|
||||
else if(formatarg->type == FF_FORMAT_ARG_TYPE_UINT8)
|
||||
ffStrbufAppendF(buffer, "%hhu", *(uint8_t*)formatarg->value);
|
||||
else if(formatarg->type == FF_FORMAT_ARG_TYPE_STRING)
|
||||
ffStrbufAppendS(buffer, (const char*)formatarg->value);
|
||||
else if(formatarg->type == FF_FORMAT_ARG_TYPE_STRBUF)
|
||||
ffStrbufAppend(buffer, (FFstrbuf*)formatarg->value);
|
||||
else if(formatarg->type == FF_FORMAT_ARG_TYPE_DOUBLE)
|
||||
ffStrbufAppendF(buffer, "%g", *(double*)formatarg->value);
|
||||
else if(formatarg->type == FF_FORMAT_ARG_TYPE_LIST)
|
||||
{
|
||||
const FFlist* list = formatarg->value;
|
||||
for(uint32_t i = 0; i < list->length; i++)
|
||||
{
|
||||
ffStrbufAppend(buffer, ffListGet(list, i));
|
||||
if(i < list->length - 1)
|
||||
ffStrbufAppendS(buffer, ", ");
|
||||
}
|
||||
}
|
||||
else if(formatarg->type != FF_FORMAT_ARG_TYPE_NULL)
|
||||
{
|
||||
fprintf(stderr, "Error: format string \"%s\": argument is not implemented: %i\n", buffer->chars, formatarg->type);
|
||||
}
|
||||
}
|
||||
|
||||
static inline uint32_t getArgumentIndex(const FFstrbuf* placeholderValue)
|
||||
{
|
||||
uint32_t result = UINT32_MAX;
|
||||
|
||||
if(placeholderValue->chars[0] != '-')
|
||||
sscanf(placeholderValue->chars, "%u", &result);
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
static inline void appendInvalidPlaceholder(FFstrbuf* buffer, const char* start, const FFstrbuf* placeholderValue, uint32_t index, uint32_t formatStringLength)
|
||||
{
|
||||
ffStrbufAppendS(buffer, start);
|
||||
ffStrbufAppend(buffer, placeholderValue);
|
||||
|
||||
if(index < formatStringLength)
|
||||
ffStrbufAppendC(buffer, '}');
|
||||
}
|
||||
|
||||
static inline void appendEmptyPlaceholder(FFstrbuf* buffer, const char* placeholder, uint32_t* argCounter, uint32_t numArgs, const FFformatarg* arguments)
|
||||
{
|
||||
if(*argCounter > numArgs)
|
||||
ffStrbufAppendS(buffer, placeholder);
|
||||
else
|
||||
ffFormatAppendFormatArg(buffer, &arguments[(*argCounter)++]);
|
||||
}
|
||||
|
||||
static inline bool formatArgSet(const FFformatarg* arg)
|
||||
{
|
||||
return arg->value != NULL && (
|
||||
(arg->type == FF_FORMAT_ARG_TYPE_DOUBLE && *(double*)arg->value > 0) ||
|
||||
(arg->type == FF_FORMAT_ARG_TYPE_DOUBLE && *(double*)arg->value == *(double*)arg->value) || //NaN
|
||||
(arg->type == FF_FORMAT_ARG_TYPE_INT && *(int*)arg->value > 0) ||
|
||||
(arg->type == FF_FORMAT_ARG_TYPE_STRBUF && ((FFstrbuf*)arg->value)->length > 0) ||
|
||||
(arg->type == FF_FORMAT_ARG_TYPE_STRING && *(const char*)arg->value != '\0') ||
|
||||
(arg->type == FF_FORMAT_ARG_TYPE_UINT8 && *(uint8_t*)arg->value > 0) ||
|
||||
(arg->type == FF_FORMAT_ARG_TYPE_UINT16 && *(uint16_t*)arg->value > 0) ||
|
||||
(arg->type == FF_FORMAT_ARG_TYPE_UINT && *(uint32_t*)arg->value > 0)
|
||||
);
|
||||
}
|
||||
|
||||
void ffParseFormatString(FFstrbuf* buffer, const FFstrbuf* formatstr, uint32_t numArgs, const FFformatarg* arguments)
|
||||
{
|
||||
uint32_t argCounter = 0;
|
||||
|
||||
uint32_t numOpenIfs = 0;
|
||||
uint32_t numOpenNotIfs = 0;
|
||||
uint32_t numOpenColors = 0;
|
||||
|
||||
for(uint32_t i = 0; i < formatstr->length; ++i)
|
||||
{
|
||||
// if we don't have a placeholder start just copy the chars over to output buffer
|
||||
if(formatstr->chars[i] != '{')
|
||||
{
|
||||
ffStrbufAppendC(buffer, formatstr->chars[i]);
|
||||
continue;
|
||||
}
|
||||
|
||||
// if we have an { at the end handle it as {}
|
||||
if(i == formatstr->length - 1)
|
||||
{
|
||||
appendEmptyPlaceholder(buffer, "{", &argCounter, numArgs, arguments);
|
||||
continue;
|
||||
}
|
||||
|
||||
// jump to next char, the start of the placeholder value
|
||||
++i;
|
||||
|
||||
// double {{ elvaluates to a single { and doesn't count as start
|
||||
if(formatstr->chars[i] == '{')
|
||||
{
|
||||
ffStrbufAppendC(buffer, '{');
|
||||
continue;
|
||||
}
|
||||
|
||||
// placeholder is {}
|
||||
if(formatstr->chars[i] == '}')
|
||||
{
|
||||
appendEmptyPlaceholder(buffer, "{}", &argCounter, numArgs, arguments);
|
||||
continue;
|
||||
}
|
||||
|
||||
FFstrbuf placeholderValue;
|
||||
ffStrbufInit(&placeholderValue);
|
||||
|
||||
while(i < formatstr->length && formatstr->chars[i] != '}')
|
||||
ffStrbufAppendC(&placeholderValue, formatstr->chars[i++]);
|
||||
|
||||
// test if for stop, if so break the loop
|
||||
if(placeholderValue.length == 1 && placeholderValue.chars[0] == '-')
|
||||
{
|
||||
ffStrbufDestroy(&placeholderValue);
|
||||
break;
|
||||
}
|
||||
|
||||
// test for end of an if, if so do nothing
|
||||
if(placeholderValue.length == 1 && placeholderValue.chars[0] == '?')
|
||||
{
|
||||
if(numOpenIfs == 0)
|
||||
appendInvalidPlaceholder(buffer, "{", &placeholderValue, i, formatstr->length);
|
||||
else
|
||||
--numOpenIfs;
|
||||
|
||||
ffStrbufDestroy(&placeholderValue);
|
||||
continue;
|
||||
}
|
||||
|
||||
// test for end of a not if, if so do nothing
|
||||
if(placeholderValue.length == 1 && placeholderValue.chars[0] == '/')
|
||||
{
|
||||
if(numOpenNotIfs == 0)
|
||||
appendInvalidPlaceholder(buffer, "{", &placeholderValue, i, formatstr->length);
|
||||
else
|
||||
--numOpenNotIfs;
|
||||
|
||||
ffStrbufDestroy(&placeholderValue);
|
||||
continue;
|
||||
}
|
||||
|
||||
// test for end of a color, if so do nothing
|
||||
if(placeholderValue.length == 1 && placeholderValue.chars[0] == '#')
|
||||
{
|
||||
if(numOpenColors == 0)
|
||||
appendInvalidPlaceholder(buffer, "{", &placeholderValue, i, formatstr->length);
|
||||
else
|
||||
{
|
||||
ffStrbufAppendS(buffer, FASTFETCH_TEXT_MODIFIER_RESET);
|
||||
--numOpenColors;
|
||||
}
|
||||
|
||||
ffStrbufDestroy(&placeholderValue);
|
||||
continue;
|
||||
}
|
||||
|
||||
// test for if, if so evaluate it
|
||||
if(placeholderValue.chars[0] == '?')
|
||||
{
|
||||
ffStrbufSubstrAfter(&placeholderValue, 0);
|
||||
|
||||
uint32_t index = getArgumentIndex(&placeholderValue);
|
||||
|
||||
// testing for an invalid index
|
||||
if(index > numArgs)
|
||||
{
|
||||
appendInvalidPlaceholder(buffer, "{?", &placeholderValue, i, formatstr->length);
|
||||
ffStrbufDestroy(&placeholderValue);
|
||||
continue;
|
||||
}
|
||||
|
||||
// continue normally if an format arg is set and the value is > 0
|
||||
if(formatArgSet(&arguments[index - 1]))
|
||||
{
|
||||
++numOpenIfs;
|
||||
ffStrbufDestroy(&placeholderValue);
|
||||
continue;
|
||||
}
|
||||
|
||||
// fastforward to the end of the if without printing the in between
|
||||
i = ffStrbufNextIndexS(formatstr, i, "{?}") + 2; // 2 is the length of "{?}" - 1 because the loop will increament it again directly after continue
|
||||
ffStrbufDestroy(&placeholderValue);
|
||||
continue;
|
||||
}
|
||||
|
||||
// test for not if, if so evaluate it
|
||||
if(placeholderValue.chars[0] == '/')
|
||||
{
|
||||
ffStrbufSubstrAfter(&placeholderValue, 0);
|
||||
|
||||
uint32_t index = getArgumentIndex(&placeholderValue);
|
||||
|
||||
// testing for an invalid index
|
||||
if(index > numArgs)
|
||||
{
|
||||
appendInvalidPlaceholder(buffer, "{/", &placeholderValue, i, formatstr->length);
|
||||
ffStrbufDestroy(&placeholderValue);
|
||||
continue;
|
||||
}
|
||||
|
||||
//continue normally if an format arg is not set or the value is 0
|
||||
if(!formatArgSet(&arguments[index - 1]))
|
||||
{
|
||||
++numOpenNotIfs;
|
||||
ffStrbufDestroy(&placeholderValue);
|
||||
continue;
|
||||
}
|
||||
|
||||
// fastforward to the end of the if without printing the in between
|
||||
i = ffStrbufNextIndexS(formatstr, i, "{/}") + 2; // 2 is the length of "{/}" - 1 because the loop will increament it again directly after continue
|
||||
ffStrbufDestroy(&placeholderValue);
|
||||
continue;
|
||||
}
|
||||
|
||||
//test for color, if so evaluate it
|
||||
if(placeholderValue.chars[0] == '#')
|
||||
{
|
||||
++numOpenColors;
|
||||
ffStrbufSubstrAfter(&placeholderValue, 0);
|
||||
ffStrbufAppendS(buffer, "\033[");
|
||||
ffStrbufAppend(buffer, &placeholderValue);
|
||||
ffStrbufAppendC(buffer, 'm');
|
||||
ffStrbufDestroy(&placeholderValue);
|
||||
continue;
|
||||
}
|
||||
|
||||
uint32_t index = getArgumentIndex(&placeholderValue);
|
||||
|
||||
// test for invalid index
|
||||
if(index > numArgs)
|
||||
{
|
||||
appendInvalidPlaceholder(buffer, "{", &placeholderValue, i, formatstr->length);
|
||||
ffStrbufDestroy(&placeholderValue);
|
||||
continue;
|
||||
}
|
||||
|
||||
ffFormatAppendFormatArg(buffer, &arguments[index - 1]);
|
||||
|
||||
ffStrbufDestroy(&placeholderValue);
|
||||
}
|
||||
|
||||
ffStrbufTrimRight(buffer, ' ');
|
||||
|
||||
ffStrbufAppendS(buffer, FASTFETCH_TEXT_MODIFIER_RESET);
|
||||
}
|
||||
@@ -1,28 +0,0 @@
|
||||
#pragma once
|
||||
|
||||
#ifndef FF_INCLUDED_common_format
|
||||
#define FF_INCLUDED_common_format
|
||||
|
||||
typedef enum FFformatargtype
|
||||
{
|
||||
FF_FORMAT_ARG_TYPE_NULL = 0,
|
||||
FF_FORMAT_ARG_TYPE_UINT,
|
||||
FF_FORMAT_ARG_TYPE_UINT16,
|
||||
FF_FORMAT_ARG_TYPE_UINT8,
|
||||
FF_FORMAT_ARG_TYPE_INT,
|
||||
FF_FORMAT_ARG_TYPE_STRING,
|
||||
FF_FORMAT_ARG_TYPE_STRBUF,
|
||||
FF_FORMAT_ARG_TYPE_DOUBLE,
|
||||
FF_FORMAT_ARG_TYPE_LIST
|
||||
} FFformatargtype;
|
||||
|
||||
typedef struct FFformatarg
|
||||
{
|
||||
FFformatargtype type;
|
||||
const void* value;
|
||||
} FFformatarg;
|
||||
|
||||
void ffFormatAppendFormatArg(FFstrbuf* buffer, const FFformatarg* formatarg);
|
||||
void ffParseFormatString(FFstrbuf* buffer, const FFstrbuf* formatstr, uint32_t numArgs, const FFformatarg* arguments);
|
||||
|
||||
#endif
|
||||
@@ -1,424 +0,0 @@
|
||||
#include "fastfetch.h"
|
||||
#include "common/caching.h"
|
||||
#include "detection/qt.h"
|
||||
#include "detection/gtk.h"
|
||||
#include "detection/displayserver.h"
|
||||
|
||||
#include <stdlib.h>
|
||||
#include <unistd.h>
|
||||
#include <sys/stat.h>
|
||||
#include <signal.h>
|
||||
#include <pthread.h>
|
||||
|
||||
static bool strbufEqualsAdapter(const void* first, const void* second)
|
||||
{
|
||||
return ffStrbufComp(second, first) == 0;
|
||||
}
|
||||
|
||||
static void initConfigDirs(FFstate* state)
|
||||
{
|
||||
ffListInit(&state->configDirs, sizeof(FFstrbuf));
|
||||
|
||||
const char* xdgConfigHome = getenv("XDG_CONFIG_HOME");
|
||||
if(xdgConfigHome != NULL)
|
||||
{
|
||||
FFstrbuf* buffer = (FFstrbuf*) ffListAdd(&state->configDirs);
|
||||
ffStrbufInitA(buffer, 64);
|
||||
ffStrbufAppendS(buffer, xdgConfigHome);
|
||||
ffStrbufEnsureEndsWithC(buffer, '/');
|
||||
}
|
||||
|
||||
#define FF_ENSURE_ONLY_ONCE_IN_LIST(element) \
|
||||
if(ffListFirstIndexComp(&state->configDirs, element, strbufEqualsAdapter) < state->configDirs.length - 1) \
|
||||
--state->configDirs.length;
|
||||
|
||||
FFstrbuf* userConfigHome = ffListAdd(&state->configDirs);
|
||||
ffStrbufInitA(userConfigHome, 64);
|
||||
ffStrbufAppendS(userConfigHome, state->passwd->pw_dir);
|
||||
ffStrbufAppendS(userConfigHome, "/.config/");
|
||||
FF_ENSURE_ONLY_ONCE_IN_LIST(userConfigHome)
|
||||
|
||||
FFstrbuf* userHome = ffListAdd(&state->configDirs);
|
||||
ffStrbufInitA(userHome, 64);
|
||||
ffStrbufAppendS(userHome, state->passwd->pw_dir);
|
||||
ffStrbufEnsureEndsWithC(userHome, '/');
|
||||
FF_ENSURE_ONLY_ONCE_IN_LIST(userHome)
|
||||
|
||||
FFstrbuf xdgConfigDirs;
|
||||
ffStrbufInitA(&xdgConfigDirs, 64);
|
||||
ffStrbufAppendS(&xdgConfigDirs, getenv("XDG_CONFIG_DIRS"));
|
||||
|
||||
uint32_t startIndex = 0;
|
||||
while (startIndex < xdgConfigDirs.length)
|
||||
{
|
||||
uint32_t colonIndex = ffStrbufNextIndexC(&xdgConfigDirs, startIndex, ':');
|
||||
xdgConfigDirs.chars[colonIndex] = '\0';
|
||||
|
||||
FFstrbuf* buffer = (FFstrbuf*) ffListAdd(&state->configDirs);
|
||||
ffStrbufInitA(buffer, 64);
|
||||
ffStrbufAppendS(buffer, xdgConfigDirs.chars + startIndex);
|
||||
ffStrbufEnsureEndsWithC(buffer, '/');
|
||||
FF_ENSURE_ONLY_ONCE_IN_LIST(buffer);
|
||||
|
||||
startIndex = colonIndex + 1;
|
||||
}
|
||||
|
||||
FFstrbuf* systemConfigHome = ffListAdd(&state->configDirs);
|
||||
ffStrbufInitA(systemConfigHome, 64);
|
||||
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, FASTFETCH_TARGET_DIR_ROOT"/etc/");
|
||||
FF_ENSURE_ONLY_ONCE_IN_LIST(systemConfig)
|
||||
|
||||
#undef FF_ENSURE_ONLY_ONCE_IN_LIST
|
||||
}
|
||||
|
||||
static void initCacheDir(FFstate* state)
|
||||
{
|
||||
ffStrbufInitA(&state->cacheDir, 64);
|
||||
|
||||
ffStrbufAppendS(&state->cacheDir, getenv("XDG_CACHE_HOME"));
|
||||
|
||||
if(state->cacheDir.length == 0)
|
||||
{
|
||||
ffStrbufAppendS(&state->cacheDir, state->passwd->pw_dir);
|
||||
ffStrbufAppendS(&state->cacheDir, "/.cache/");
|
||||
}
|
||||
else
|
||||
ffStrbufEnsureEndsWithC(&state->cacheDir, '/');
|
||||
|
||||
mkdir(state->cacheDir.chars, S_IRWXU | S_IXGRP | S_IRGRP | S_IXOTH | S_IROTH); //I hope everybody has a cache folder, but who knows
|
||||
|
||||
ffStrbufAppendS(&state->cacheDir, "fastfetch/");
|
||||
mkdir(state->cacheDir.chars, S_IRWXU | S_IRGRP | S_IROTH);
|
||||
}
|
||||
|
||||
static void initState(FFstate* state)
|
||||
{
|
||||
state->logoWidth = 0;
|
||||
state->logoHeight = 0;
|
||||
state->keysHeight = 0;
|
||||
state->passwd = getpwuid(getuid());
|
||||
uname(&state->utsname);
|
||||
sysinfo(&state->sysinfo);
|
||||
|
||||
initConfigDirs(state);
|
||||
initCacheDir(state);
|
||||
}
|
||||
|
||||
static void initModuleArg(FFModuleArgs* args)
|
||||
{
|
||||
ffStrbufInitA(&args->key, 0);
|
||||
ffStrbufInitA(&args->outputFormat, 0);
|
||||
ffStrbufInitA(&args->errorFormat, 0);
|
||||
}
|
||||
|
||||
static void defaultConfig(FFinstance* instance)
|
||||
{
|
||||
ffStrbufInit(&instance->config.logo.source);
|
||||
instance->config.logo.type = FF_LOGO_TYPE_AUTO;
|
||||
for(uint8_t i = 0; i < (uint8_t) FASTFETCH_LOGO_MAX_COLORS; ++i)
|
||||
ffStrbufInit(&instance->config.logo.colors[i]);
|
||||
instance->config.logo.width = 0;
|
||||
instance->config.logo.height = 0; //preserve aspect ratio
|
||||
instance->config.logo.paddingLeft = 0;
|
||||
instance->config.logo.paddingRight = 4;
|
||||
instance->config.logo.printRemaining = true;
|
||||
|
||||
ffStrbufInit(&instance->config.mainColor);
|
||||
ffStrbufInit(&instance->config.separator);
|
||||
ffStrbufAppendS(&instance->config.separator, ": ");
|
||||
|
||||
instance->config.showErrors = false;
|
||||
instance->config.recache = false;
|
||||
instance->config.cacheSave = true;
|
||||
instance->config.allowSlowOperations = false;
|
||||
instance->config.disableLinewrap = true;
|
||||
instance->config.hideCursor = true;
|
||||
instance->config.escapeBedrock = true;
|
||||
instance->config.glType = FF_GL_TYPE_AUTO;
|
||||
instance->config.pipe = false;
|
||||
instance->config.multithreading = true;
|
||||
|
||||
initModuleArg(&instance->config.os);
|
||||
initModuleArg(&instance->config.host);
|
||||
initModuleArg(&instance->config.kernel);
|
||||
initModuleArg(&instance->config.uptime);
|
||||
initModuleArg(&instance->config.processes);
|
||||
initModuleArg(&instance->config.packages);
|
||||
initModuleArg(&instance->config.shell);
|
||||
initModuleArg(&instance->config.resolution);
|
||||
initModuleArg(&instance->config.de);
|
||||
initModuleArg(&instance->config.wm);
|
||||
initModuleArg(&instance->config.wmTheme);
|
||||
initModuleArg(&instance->config.theme);
|
||||
initModuleArg(&instance->config.icons);
|
||||
initModuleArg(&instance->config.font);
|
||||
initModuleArg(&instance->config.cursor);
|
||||
initModuleArg(&instance->config.terminal);
|
||||
initModuleArg(&instance->config.terminalFont);
|
||||
initModuleArg(&instance->config.cpu);
|
||||
initModuleArg(&instance->config.cpuUsage);
|
||||
initModuleArg(&instance->config.gpu);
|
||||
initModuleArg(&instance->config.memory);
|
||||
initModuleArg(&instance->config.disk);
|
||||
initModuleArg(&instance->config.battery);
|
||||
initModuleArg(&instance->config.locale);
|
||||
initModuleArg(&instance->config.localIP);
|
||||
initModuleArg(&instance->config.publicIP);
|
||||
initModuleArg(&instance->config.player);
|
||||
initModuleArg(&instance->config.song);
|
||||
initModuleArg(&instance->config.dateTime);
|
||||
initModuleArg(&instance->config.date);
|
||||
initModuleArg(&instance->config.time);
|
||||
initModuleArg(&instance->config.vulkan);
|
||||
initModuleArg(&instance->config.openGL);
|
||||
initModuleArg(&instance->config.openCL);
|
||||
|
||||
ffStrbufInitA(&instance->config.libPCI, 0);
|
||||
ffStrbufInitA(&instance->config.libVulkan, 0);
|
||||
ffStrbufInitA(&instance->config.libWayland, 0);
|
||||
ffStrbufInitA(&instance->config.libXcbRandr, 0);
|
||||
ffStrbufInitA(&instance->config.libXcb, 0);
|
||||
ffStrbufInitA(&instance->config.libXrandr, 0);
|
||||
ffStrbufInitA(&instance->config.libX11, 0);
|
||||
ffStrbufInitA(&instance->config.libGIO, 0);
|
||||
ffStrbufInitA(&instance->config.libDConf, 0);
|
||||
ffStrbufInitA(&instance->config.libDBus, 0);
|
||||
ffStrbufInitA(&instance->config.libXFConf, 0);
|
||||
ffStrbufInitA(&instance->config.libSQLite3, 0);
|
||||
ffStrbufInitA(&instance->config.librpm, 0);
|
||||
ffStrbufInitA(&instance->config.libImageMagick, 0);
|
||||
ffStrbufInitA(&instance->config.libZ, 0);
|
||||
ffStrbufInitA(&instance->config.libChafa, 0);
|
||||
ffStrbufInitA(&instance->config.libEGL, 0);
|
||||
ffStrbufInitA(&instance->config.libGLX, 0);
|
||||
ffStrbufInitA(&instance->config.libOSMesa, 0);
|
||||
ffStrbufInitA(&instance->config.libOpenCL, 0);
|
||||
|
||||
instance->config.titleFQDN = false;
|
||||
|
||||
ffStrbufInitA(&instance->config.diskFolders, 0);
|
||||
|
||||
ffStrbufInitA(&instance->config.batteryDir, 0);
|
||||
|
||||
ffStrbufInitA(&instance->config.separatorString, 0);
|
||||
|
||||
instance->config.localIpShowIpV4 = true;
|
||||
instance->config.localIpShowIpV6 = false;
|
||||
instance->config.localIpShowLoop = false;
|
||||
|
||||
instance->config.publicIpTimeout = 0;
|
||||
|
||||
ffStrbufInitA(&instance->config.osFile, 0);
|
||||
|
||||
ffStrbufInitA(&instance->config.playerName, 0);
|
||||
}
|
||||
|
||||
void ffInitInstance(FFinstance* instance)
|
||||
{
|
||||
initState(&instance->state);
|
||||
defaultConfig(instance);
|
||||
}
|
||||
|
||||
static inline void* detectPlasmaThreadMain(void* instance)
|
||||
{
|
||||
ffDetectQt((FFinstance*)instance);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
static inline void* detectGTK2ThreadMain(void* instance)
|
||||
{
|
||||
ffDetectGTK2((FFinstance*)instance);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
static inline void* detectGTK3ThreadMain(void* instance)
|
||||
{
|
||||
ffDetectGTK3((FFinstance*)instance);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
static inline void* detectGTK4ThreadMain(void* instance)
|
||||
{
|
||||
ffDetectGTK4((FFinstance*)instance);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
static inline void* connectDisplayServerThreadMain(void* instance)
|
||||
{
|
||||
ffConnectDisplayServer((FFinstance*)instance);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
static inline void* startThreadsThreadMain(void* instance)
|
||||
{
|
||||
pthread_t dsThread;
|
||||
pthread_create(&dsThread, NULL, connectDisplayServerThreadMain, instance);
|
||||
pthread_detach(dsThread);
|
||||
|
||||
pthread_t gtk2Thread;
|
||||
pthread_create(>k2Thread, NULL, detectGTK2ThreadMain, instance);
|
||||
pthread_detach(gtk2Thread);
|
||||
|
||||
pthread_t gtk3Thread;
|
||||
pthread_create(>k3Thread, NULL, detectGTK3ThreadMain, instance);
|
||||
pthread_detach(gtk3Thread);
|
||||
|
||||
pthread_t gtk4Thread;
|
||||
pthread_create(>k4Thread, NULL, detectGTK4ThreadMain, instance);
|
||||
pthread_detach(gtk4Thread);
|
||||
|
||||
pthread_t plasmaThread;
|
||||
pthread_create(&plasmaThread, NULL, detectPlasmaThreadMain, instance);
|
||||
pthread_detach(plasmaThread);
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
void startDetectionThreads(FFinstance* instance)
|
||||
{
|
||||
//Android needs none of the things that are detected here
|
||||
//And using gsettings sometimes hangs the program in android for some unknown reason,
|
||||
//and since we don't need it we just never call it.
|
||||
#ifdef __ANDROID__
|
||||
return;
|
||||
#endif
|
||||
|
||||
pthread_t startThreadsThread;
|
||||
pthread_create(&startThreadsThread, NULL, startThreadsThreadMain, instance);
|
||||
pthread_detach(startThreadsThread);
|
||||
}
|
||||
|
||||
static volatile bool ffDisableLinewrap = true;
|
||||
static volatile bool ffHideCursor = true;
|
||||
|
||||
static void resetConsole()
|
||||
{
|
||||
if(ffDisableLinewrap)
|
||||
fputs("\033[?7h", stdout);
|
||||
|
||||
if(ffHideCursor)
|
||||
fputs("\033[?25h", stdout);
|
||||
}
|
||||
|
||||
static void exitSignalHandler(int signal)
|
||||
{
|
||||
FF_UNUSED(signal);
|
||||
resetConsole();
|
||||
exit(0);
|
||||
}
|
||||
|
||||
void ffStart(FFinstance* instance)
|
||||
{
|
||||
if(instance->config.multithreading)
|
||||
startDetectionThreads(instance);
|
||||
|
||||
ffDisableLinewrap = instance->config.disableLinewrap && !instance->config.pipe;
|
||||
ffHideCursor = instance->config.hideCursor && !instance->config.pipe;
|
||||
|
||||
struct sigaction action = {};
|
||||
action.sa_handler = exitSignalHandler;
|
||||
|
||||
sigaction(SIGINT, &action, NULL);
|
||||
sigaction(SIGTERM, &action, NULL);
|
||||
sigaction(SIGQUIT, &action, NULL);
|
||||
|
||||
//We do the cache validation here, so we can skip it if --recache is given
|
||||
if(!instance->config.recache)
|
||||
ffCacheValidate(instance);
|
||||
|
||||
//reset everything to default before we start printing
|
||||
if(!instance->config.pipe)
|
||||
fputs(FASTFETCH_TEXT_MODIFIER_RESET, stdout);
|
||||
|
||||
if(ffHideCursor)
|
||||
fputs("\033[?25l", stdout);
|
||||
|
||||
if(ffDisableLinewrap)
|
||||
fputs("\033[?7l", stdout);
|
||||
|
||||
ffLogoPrint(instance);
|
||||
}
|
||||
|
||||
void ffFinish(FFinstance* instance)
|
||||
{
|
||||
if(instance->config.logo.printRemaining)
|
||||
ffLogoPrintRemaining(instance);
|
||||
|
||||
resetConsole();
|
||||
}
|
||||
|
||||
//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(
|
||||
#ifdef FF_HAVE_LIBPCI
|
||||
"libpci\n"
|
||||
#endif
|
||||
#ifdef FF_HAVE_VULKAN
|
||||
"vulkan\n"
|
||||
#endif
|
||||
#ifdef FF_HAVE_WAYLAND
|
||||
"wayland\n"
|
||||
#endif
|
||||
#ifdef FF_HAVE_XCB_RANDR
|
||||
"xcb-randr\n"
|
||||
#endif
|
||||
#ifdef FF_HAVE_XCB
|
||||
"xcb\n"
|
||||
#endif
|
||||
#ifdef FF_HAVE_XRANDR
|
||||
"xrandr\n"
|
||||
#endif
|
||||
#ifdef FF_HAVE_X11
|
||||
"x11\n"
|
||||
#endif
|
||||
#ifdef FF_HAVE_GIO
|
||||
"gio\n"
|
||||
#endif
|
||||
#ifdef FF_HAVE_DCONF
|
||||
"dconf\n"
|
||||
#endif
|
||||
#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_SQLITE3
|
||||
"sqlite3\n"
|
||||
#endif
|
||||
#ifdef FF_HAVE_RPM
|
||||
"rpm\n"
|
||||
#endif
|
||||
#ifdef FF_HAVE_EGL
|
||||
"egl\n"
|
||||
#endif
|
||||
#ifdef FF_HAVE_GLX
|
||||
"glx\n"
|
||||
#endif
|
||||
#ifdef FF_HAVE_OSMESA
|
||||
"osmesa\n"
|
||||
#endif
|
||||
#ifdef FF_HAVE_OPENCL
|
||||
"opencl\n"
|
||||
#endif
|
||||
""
|
||||
, stdout);
|
||||
}
|
||||
-193
@@ -1,193 +0,0 @@
|
||||
#include "fastfetch.h"
|
||||
#include "common/io.h"
|
||||
|
||||
#include <sys/stat.h>
|
||||
#include <unistd.h>
|
||||
#include <fcntl.h>
|
||||
#include <termios.h>
|
||||
#include <poll.h>
|
||||
|
||||
static void createSubfolders(const char* fileName)
|
||||
{
|
||||
FFstrbuf path;
|
||||
ffStrbufInit(&path);
|
||||
|
||||
while(*fileName != '\0')
|
||||
{
|
||||
ffStrbufAppendC(&path, *fileName);
|
||||
if(*fileName == '/')
|
||||
mkdir(path.chars, S_IRWXU | S_IRGRP | S_IROTH);
|
||||
++fileName;
|
||||
}
|
||||
|
||||
ffStrbufDestroy(&path);
|
||||
}
|
||||
|
||||
|
||||
bool ffWriteFDBuffer(int fd, const FFstrbuf* content)
|
||||
{
|
||||
return write(fd, content->chars, content->length) != -1;
|
||||
}
|
||||
|
||||
bool ffWriteFileData(const char* fileName, size_t dataSize, const void* data)
|
||||
{
|
||||
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 = write(fd, data, dataSize) != -1;
|
||||
|
||||
close(fd);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
bool ffWriteFileBuffer(const char* fileName, const FFstrbuf* buffer)
|
||||
{
|
||||
return ffWriteFileData(fileName, buffer->length, buffer->chars);
|
||||
}
|
||||
|
||||
bool ffAppendFDBuffer(int fd, FFstrbuf* buffer)
|
||||
{
|
||||
ssize_t readed = 0;
|
||||
|
||||
ffStrbufEnsureFree(buffer, 31); // 32 - 1 for the null terminator
|
||||
uint32_t free = ffStrbufGetFree(buffer);
|
||||
|
||||
while(
|
||||
(readed = read(fd, buffer->chars + buffer->length, free)) > 0 &&
|
||||
(uint32_t) readed == free
|
||||
) {
|
||||
buffer->length += (uint32_t) readed;
|
||||
ffStrbufEnsureFree(buffer, buffer->allocated - 1); // Doubles capacity every round. -1 for the null byte.
|
||||
free = ffStrbufGetFree(buffer);
|
||||
}
|
||||
|
||||
// In case of failure, read returns -1. We don't want to substract the length of the buffer.
|
||||
if(readed > 0)
|
||||
buffer->length += (uint32_t) readed;
|
||||
|
||||
buffer->chars[buffer->length] = '\0';
|
||||
|
||||
ffStrbufTrimRight(buffer, '\n');
|
||||
ffStrbufTrimRight(buffer, ' ');
|
||||
|
||||
return readed >= 0;
|
||||
}
|
||||
|
||||
ssize_t ffReadFileData(const char* fileName, size_t dataSize, void* data)
|
||||
{
|
||||
int fd = open(fileName, O_RDONLY);
|
||||
if(fd == -1)
|
||||
return -1;
|
||||
|
||||
ssize_t readed = read(fd, data, dataSize);
|
||||
|
||||
close(fd);
|
||||
|
||||
return readed;
|
||||
}
|
||||
|
||||
bool ffAppendFileBuffer(const char* fileName, FFstrbuf* buffer)
|
||||
{
|
||||
int fd = open(fileName, O_RDONLY);
|
||||
if(fd == -1)
|
||||
return false;
|
||||
|
||||
bool ret = ffAppendFDBuffer(fd, buffer);
|
||||
|
||||
close(fd);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
bool ffReadFileBuffer(const char* fileName, FFstrbuf* buffer)
|
||||
{
|
||||
ffStrbufClear(buffer);
|
||||
return ffAppendFileBuffer(fileName, buffer);
|
||||
}
|
||||
|
||||
// Not thread safe!
|
||||
void ffSuppressIO(bool suppress)
|
||||
{
|
||||
static bool init = false;
|
||||
static int origOut = -1;
|
||||
static int origErr = -1;
|
||||
static int nullFile = -1;
|
||||
|
||||
if(!init)
|
||||
{
|
||||
if(!suppress)
|
||||
return;
|
||||
|
||||
origOut = dup(STDOUT_FILENO);
|
||||
origErr = dup(STDERR_FILENO);
|
||||
nullFile = open("/dev/null", O_WRONLY);
|
||||
init = true;
|
||||
}
|
||||
|
||||
if(nullFile == -1)
|
||||
return;
|
||||
|
||||
fflush(stdout);
|
||||
fflush(stderr);
|
||||
|
||||
dup2(suppress ? nullFile : origOut, STDOUT_FILENO);
|
||||
dup2(suppress ? nullFile : origErr, STDERR_FILENO);
|
||||
}
|
||||
|
||||
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);
|
||||
}
|
||||
@@ -1,24 +0,0 @@
|
||||
#pragma once
|
||||
|
||||
#ifndef FF_INCLUDED_common_io
|
||||
#define FF_INCLUDED_common_io
|
||||
|
||||
#include <sys/types.h> //mode_t
|
||||
|
||||
bool ffWriteFDBuffer(int fd, const FFstrbuf* content);
|
||||
bool ffWriteFileData(const char* fileName, size_t dataSize, const void* data);
|
||||
bool ffWriteFileBuffer(const char* fileName, const FFstrbuf* buffer);
|
||||
|
||||
bool ffAppendFDBuffer(int fd, FFstrbuf* buffer);
|
||||
ssize_t ffReadFileData(const char* fileName, size_t dataSize, void* data);
|
||||
bool ffAppendFileBuffer(const char* fileName, FFstrbuf* buffer);
|
||||
bool ffReadFileBuffer(const char* fileName, FFstrbuf* buffer);
|
||||
|
||||
bool ffFileExists(const char* fileName, mode_t mode);
|
||||
|
||||
// Not thread safe!
|
||||
void ffSuppressIO(bool suppress);
|
||||
|
||||
void ffGetTerminalResponse(const char* request, const char* format, ...);
|
||||
|
||||
#endif
|
||||
@@ -1,56 +0,0 @@
|
||||
#include "fastfetch.h"
|
||||
#include "common/library.h"
|
||||
|
||||
#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)
|
||||
return dlopen(userProvidedName->chars, RTLD_LAZY);
|
||||
|
||||
va_list defaultNames;
|
||||
va_start(defaultNames, userProvidedName);
|
||||
|
||||
void* result = NULL;
|
||||
|
||||
while(result == NULL)
|
||||
{
|
||||
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);
|
||||
|
||||
return result;
|
||||
}
|
||||
@@ -1,29 +0,0 @@
|
||||
#pragma once
|
||||
|
||||
#ifndef FF_INCLUDED_common_library
|
||||
#define FF_INCLUDED_common_library
|
||||
|
||||
#include <dlfcn.h>
|
||||
|
||||
#define FF_LIBRARY_SYMBOL(symbolName) \
|
||||
__typeof__(&symbolName) ff ## symbolName;
|
||||
|
||||
#define FF_LIBRARY_LOAD(libraryObjectName, userLibraryName, returnValue, ...) \
|
||||
void* libraryObjectName = ffLibraryLoad(&userLibraryName, __VA_ARGS__, NULL);\
|
||||
if(libraryObjectName == NULL) \
|
||||
return returnValue;
|
||||
|
||||
#define FF_LIBRARY_LOAD_SYMBOL_ADRESS(library, symbolMapping, symbolName, returnValue) \
|
||||
symbolMapping = dlsym(library, #symbolName); \
|
||||
if(symbolMapping == NULL) \
|
||||
{ \
|
||||
dlclose(library); \
|
||||
return returnValue; \
|
||||
}
|
||||
|
||||
#define FF_LIBRARY_LOAD_SYMBOL(library, symbolName, returnValue) \
|
||||
__typeof__(&symbolName) FF_LIBRARY_LOAD_SYMBOL_ADRESS(library, ff ## symbolName, symbolName, returnValue);
|
||||
|
||||
void* ffLibraryLoad(const FFstrbuf* userProvidedName, ...);
|
||||
|
||||
#endif
|
||||
@@ -1,69 +0,0 @@
|
||||
#include "fastfetch.h"
|
||||
#include "common/networking.h"
|
||||
|
||||
#include <unistd.h>
|
||||
#include <sys/socket.h>
|
||||
#include <netdb.h>
|
||||
|
||||
void ffNetworkingGetHttp(const char* host, const char* path, uint32_t timeout, FFstrbuf* buffer)
|
||||
{
|
||||
struct addrinfo hints = {0};
|
||||
hints.ai_family = AF_INET;
|
||||
hints.ai_socktype = SOCK_STREAM;
|
||||
hints.ai_protocol = 0;
|
||||
|
||||
struct addrinfo* addr;
|
||||
|
||||
if(getaddrinfo(host, "80", &hints, &addr) != 0)
|
||||
return;
|
||||
|
||||
int sock = socket(addr->ai_family, addr->ai_socktype, addr->ai_protocol);
|
||||
if(sock == -1)
|
||||
{
|
||||
freeaddrinfo(addr);
|
||||
return;
|
||||
}
|
||||
|
||||
if(timeout > 0)
|
||||
{
|
||||
struct timeval timev;
|
||||
timev.tv_sec = 0;
|
||||
timev.tv_usec = timeout * 1000; //milliseconds to microseconds
|
||||
setsockopt(sock, SOL_SOCKET, SO_RCVTIMEO, &timev, sizeof(timev));
|
||||
}
|
||||
|
||||
if(connect(sock, addr->ai_addr, addr->ai_addrlen) == -1)
|
||||
{
|
||||
close(sock);
|
||||
freeaddrinfo(addr);
|
||||
return;
|
||||
}
|
||||
|
||||
freeaddrinfo(addr);
|
||||
|
||||
FFstrbuf command;
|
||||
ffStrbufInitA(&command, 64);
|
||||
ffStrbufAppendS(&command, "GET ");
|
||||
ffStrbufAppendS(&command, path);
|
||||
ffStrbufAppendS(&command, " HTTP/1.1\nHost: ");
|
||||
ffStrbufAppendS(&command, host);
|
||||
ffStrbufAppendS(&command, "\r\n\r\n");
|
||||
|
||||
if(send(sock, command.chars, command.length, 0) == -1)
|
||||
{
|
||||
ffStrbufDestroy(&command);
|
||||
close(sock);
|
||||
return;
|
||||
}
|
||||
|
||||
ssize_t received = recv(sock, buffer->chars + buffer->length, ffStrbufGetFree(buffer), 0);
|
||||
|
||||
if(received > 0)
|
||||
{
|
||||
buffer->length += (uint32_t) received;
|
||||
buffer->chars[buffer->length] = '\0';
|
||||
}
|
||||
|
||||
ffStrbufDestroy(&command);
|
||||
close(sock);
|
||||
}
|
||||
@@ -1,10 +0,0 @@
|
||||
#pragma once
|
||||
|
||||
#ifndef FF_INCLUDED_common_networking
|
||||
#define FF_INCLUDED_common_networking
|
||||
|
||||
#include "util/FFstrbuf.h"
|
||||
|
||||
void ffNetworkingGetHttp(const char* host, const char* path, uint32_t timeout, FFstrbuf* buffer);
|
||||
|
||||
#endif
|
||||
@@ -1,147 +0,0 @@
|
||||
#include "fastfetch.h"
|
||||
#include "common/parsing.h"
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
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);
|
||||
}
|
||||
|
||||
int8_t ffVersionCompare(const FFVersion* version1, const FFVersion* version2)
|
||||
{
|
||||
if(version1->major != version2->major)
|
||||
return version1->major > version2->major ? 1 : -1;
|
||||
|
||||
if(version1->minor != version2->minor)
|
||||
return version1->minor > version2->minor ? 1 : -1;
|
||||
|
||||
if(version1->patch != version2->patch)
|
||||
return version1->patch > version2->patch ? 1 : -1;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
void ffVersionToPretty(const FFVersion* version, FFstrbuf* pretty)
|
||||
{
|
||||
if(version->major > 0 || version->minor > 0 || version->patch > 0)
|
||||
ffStrbufAppendF(pretty, "%u", version->major);
|
||||
|
||||
if(version->minor > 0 || version->patch > 0)
|
||||
ffStrbufAppendF(pretty, ".%u", version->minor);
|
||||
|
||||
if(version->patch > 0)
|
||||
ffStrbufAppendF(pretty, ".%u", version->patch);
|
||||
}
|
||||
|
||||
void ffParseGTK(FFstrbuf* buffer, const FFstrbuf* gtk2, const FFstrbuf* gtk3, const FFstrbuf* gtk4)
|
||||
{
|
||||
if(gtk2->length > 0 && gtk3->length > 0 && gtk4->length > 0)
|
||||
{
|
||||
if((ffStrbufIgnCaseComp(gtk2, gtk3) == 0) && (ffStrbufIgnCaseComp(gtk2, gtk4) == 0))
|
||||
{
|
||||
ffStrbufAppend(buffer, gtk4);
|
||||
ffStrbufAppendS(buffer, " [GTK2/3/4]");
|
||||
}
|
||||
else if(ffStrbufIgnCaseComp(gtk2, gtk3) == 0)
|
||||
{
|
||||
ffStrbufAppend(buffer, gtk3);
|
||||
ffStrbufAppendS(buffer, " [GTK2/3], ");
|
||||
ffStrbufAppend(buffer, gtk4);
|
||||
ffStrbufAppendS(buffer, " [GTK4]");
|
||||
}
|
||||
else if(ffStrbufIgnCaseComp(gtk3, gtk4) == 0)
|
||||
{
|
||||
ffStrbufAppend(buffer, gtk2);
|
||||
ffStrbufAppendS(buffer, " [GTK2], ");
|
||||
ffStrbufAppend(buffer, gtk4);
|
||||
ffStrbufAppendS(buffer, " [GTK3/4]");
|
||||
}
|
||||
else
|
||||
{
|
||||
ffStrbufAppend(buffer, gtk2);
|
||||
ffStrbufAppendS(buffer, " [GTK2], ");
|
||||
ffStrbufAppend(buffer, gtk3);
|
||||
ffStrbufAppendS(buffer, " [GTK3], ");
|
||||
ffStrbufAppend(buffer, gtk4);
|
||||
ffStrbufAppendS(buffer, " [GTK4]");
|
||||
}
|
||||
}
|
||||
else if(gtk2->length > 0 && gtk3->length > 0)
|
||||
{
|
||||
if(ffStrbufIgnCaseComp(gtk2, gtk3) == 0)
|
||||
{
|
||||
ffStrbufAppend(buffer, gtk3);
|
||||
ffStrbufAppendS(buffer, " [GTK2/3]");
|
||||
}
|
||||
else
|
||||
{
|
||||
ffStrbufAppend(buffer, gtk2);
|
||||
ffStrbufAppendS(buffer, " [GTK2], ");
|
||||
ffStrbufAppend(buffer, gtk3);
|
||||
ffStrbufAppendS(buffer, " [GTK3]");
|
||||
}
|
||||
}
|
||||
else if(gtk3->length > 0 && gtk4->length > 0)
|
||||
{
|
||||
if(ffStrbufIgnCaseComp(gtk3, gtk4) == 0)
|
||||
{
|
||||
ffStrbufAppend(buffer, gtk4);
|
||||
ffStrbufAppendS(buffer, " [GTK3/4]");
|
||||
}
|
||||
else
|
||||
{
|
||||
ffStrbufAppend(buffer, gtk3);
|
||||
ffStrbufAppendS(buffer, " [GTK3], ");
|
||||
ffStrbufAppend(buffer, gtk4);
|
||||
ffStrbufAppendS(buffer, " [GTK4]");
|
||||
}
|
||||
}
|
||||
else if(gtk2->length > 0)
|
||||
{
|
||||
ffStrbufAppend(buffer, gtk2);
|
||||
ffStrbufAppendS(buffer, " [GTK2]");
|
||||
}
|
||||
else if(gtk3->length > 0)
|
||||
{
|
||||
ffStrbufAppend(buffer, gtk3);
|
||||
ffStrbufAppendS(buffer, " [GTK3]");
|
||||
}
|
||||
else if(gtk4->length > 0)
|
||||
{
|
||||
ffStrbufAppend(buffer, gtk4);
|
||||
ffStrbufAppendS(buffer, " [GTK4]");
|
||||
}
|
||||
}
|
||||
@@ -1,24 +0,0 @@
|
||||
#pragma once
|
||||
|
||||
#ifndef FF_INCLUDED_parsing_h
|
||||
#define FF_INCLUDED_parsing_h
|
||||
|
||||
#include <stdint.h>
|
||||
|
||||
typedef struct FFVersion
|
||||
{
|
||||
uint32_t major;
|
||||
uint32_t minor;
|
||||
uint32_t patch;
|
||||
} FFVersion;
|
||||
|
||||
#define FF_VERSION_INIT ((FFVersion) {0})
|
||||
|
||||
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);
|
||||
|
||||
void ffVersionToPretty(const FFVersion* version, FFstrbuf* pretty);
|
||||
int8_t ffVersionCompare(const FFVersion* version1, const FFVersion* version2);
|
||||
|
||||
#endif
|
||||
@@ -1,127 +0,0 @@
|
||||
#include "fastfetch.h"
|
||||
#include "common/printing.h"
|
||||
|
||||
void ffPrintLogoAndKey(FFinstance* instance, const char* moduleName, uint8_t moduleIndex, const FFstrbuf* customKeyFormat)
|
||||
{
|
||||
ffLogoPrintLine(instance);
|
||||
|
||||
if(!instance->config.pipe)
|
||||
{
|
||||
fputs(FASTFETCH_TEXT_MODIFIER_RESET FASTFETCH_TEXT_MODIFIER_BOLT, stdout);
|
||||
ffPrintColor(&instance->config.mainColor);
|
||||
}
|
||||
|
||||
//NULL check is required for modules with custom keys, e.g. disk with the folder path
|
||||
if(customKeyFormat == NULL || customKeyFormat->length == 0)
|
||||
{
|
||||
fputs(moduleName, stdout);
|
||||
|
||||
if(moduleIndex > 0)
|
||||
printf(" %hhu", moduleIndex);
|
||||
}
|
||||
else
|
||||
{
|
||||
FFstrbuf key;
|
||||
ffStrbufInit(&key);
|
||||
ffParseFormatString(&key, customKeyFormat, 1, (FFformatarg[]){
|
||||
{FF_FORMAT_ARG_TYPE_UINT8, &moduleIndex}
|
||||
});
|
||||
ffStrbufWriteTo(&key, stdout);
|
||||
ffStrbufDestroy(&key);
|
||||
}
|
||||
|
||||
if(!instance->config.pipe)
|
||||
fputs(FASTFETCH_TEXT_MODIFIER_RESET, stdout);
|
||||
|
||||
ffStrbufWriteTo(&instance->config.separator, stdout);
|
||||
|
||||
if(!instance->config.pipe)
|
||||
fputs(FASTFETCH_TEXT_MODIFIER_RESET, stdout);
|
||||
}
|
||||
|
||||
void ffPrintFormatString(FFinstance* instance, const char* moduleName, uint8_t moduleIndex, const FFstrbuf* customKeyFormat, const FFstrbuf* format, uint32_t numArgs, const FFformatarg* arguments)
|
||||
{
|
||||
FFstrbuf buffer;
|
||||
ffStrbufInitA(&buffer, 256);
|
||||
|
||||
ffParseFormatString(&buffer, format, numArgs, arguments);
|
||||
|
||||
if(buffer.length > 0)
|
||||
{
|
||||
ffPrintLogoAndKey(instance, moduleName, moduleIndex, customKeyFormat);
|
||||
ffStrbufPutTo(&buffer, stdout);
|
||||
}
|
||||
|
||||
ffStrbufDestroy(&buffer);
|
||||
}
|
||||
|
||||
void ffPrintFormat(FFinstance* instance, const char* moduleName, uint8_t moduleIndex, const FFModuleArgs* moduleArgs, uint32_t numArgs, const FFformatarg* arguments)
|
||||
{
|
||||
ffPrintFormatString(instance, moduleName, moduleIndex, &moduleArgs->key, &moduleArgs->outputFormat, numArgs, arguments);
|
||||
}
|
||||
|
||||
static void printError(FFinstance* instance, const char* moduleName, uint8_t moduleIndex, const FFstrbuf* customKeyFormat, const FFstrbuf* customErrorFormat, const char* message, va_list arguments)
|
||||
{
|
||||
bool hasCustomErrorFormat = customErrorFormat != NULL && customErrorFormat->length > 0;
|
||||
if(!hasCustomErrorFormat && !instance->config.showErrors)
|
||||
return;
|
||||
|
||||
if(hasCustomErrorFormat)
|
||||
{
|
||||
FFstrbuf error;
|
||||
ffStrbufInit(&error);
|
||||
ffStrbufAppendVF(&error, message, arguments);
|
||||
|
||||
ffPrintFormatString(instance, moduleName, moduleIndex, customKeyFormat, customErrorFormat, 1, (FFformatarg[]) {
|
||||
{FF_FORMAT_ARG_TYPE_STRBUF, &error}
|
||||
});
|
||||
|
||||
ffStrbufDestroy(&error);
|
||||
}
|
||||
else
|
||||
{
|
||||
ffPrintLogoAndKey(instance, moduleName, moduleIndex, customKeyFormat);
|
||||
|
||||
if(!instance->config.pipe)
|
||||
fputs(FASTFETCH_TEXT_MODIFIER_ERROR, stdout);
|
||||
|
||||
vprintf(message, arguments);
|
||||
|
||||
if(!instance->config.pipe)
|
||||
puts(FASTFETCH_TEXT_MODIFIER_RESET);
|
||||
}
|
||||
}
|
||||
|
||||
void ffPrintErrorString(FFinstance* instance, const char* moduleName, uint8_t moduleIndex, const FFstrbuf* customKeyFormat, const FFstrbuf* customErrorFormat, const char* message, ...)
|
||||
{
|
||||
va_list arguments;
|
||||
va_start(arguments, message);
|
||||
printError(instance, moduleName, moduleIndex, customKeyFormat, customErrorFormat, message, arguments);
|
||||
va_end(arguments);
|
||||
}
|
||||
|
||||
void ffPrintError(FFinstance* instance, const char* moduleName, uint8_t moduleIndex, const FFModuleArgs* moduleArgs, const char* message, ...)
|
||||
{
|
||||
va_list arguments;
|
||||
va_start(arguments, message);
|
||||
printError(instance, moduleName, moduleIndex, &moduleArgs->key, &moduleArgs->errorFormat, message, arguments);
|
||||
va_end(arguments);
|
||||
}
|
||||
|
||||
void ffPrintColor(const FFstrbuf* colorValue)
|
||||
{
|
||||
//If the color is not set, this would reset in \033[m, which resets everything.
|
||||
//So we only print it, if the main color is at least one char.
|
||||
if(colorValue->length == 0)
|
||||
return;
|
||||
|
||||
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);
|
||||
}
|
||||
@@ -1,17 +0,0 @@
|
||||
#pragma once
|
||||
|
||||
#ifndef FF_INCLUDED_common_printing
|
||||
#define FF_INCLUDED_common_printing
|
||||
|
||||
#include "fastfetch.h"
|
||||
#include "common/format.h"
|
||||
|
||||
void ffPrintLogoAndKey(FFinstance* instance, const char* moduleName, uint8_t moduleIndex, const FFstrbuf* customKeyFormat);
|
||||
void ffPrintFormatString(FFinstance* instance, const char* moduleName, uint8_t moduleIndex, const FFstrbuf* customKeyFormat, const FFstrbuf* format, uint32_t numArgs, const FFformatarg* arguments);
|
||||
void ffPrintFormat(FFinstance* instance, const char* moduleName, uint8_t moduleIndex, const FFModuleArgs* moduleArgs, uint32_t numArgs, const FFformatarg* arguments);
|
||||
void ffPrintErrorString(FFinstance* instance, const char* moduleName, uint8_t moduleIndex, const FFstrbuf* customKeyFormat, const FFstrbuf* customErrorFormat, const char* message, ...);
|
||||
void ffPrintError(FFinstance* instance, const char* moduleName, uint8_t moduleIndex, const FFModuleArgs* moduleArgs, const char* message, ...);
|
||||
void ffPrintColor(const FFstrbuf* colorValue);
|
||||
void ffPrintCharTimes(char c, uint32_t times);
|
||||
|
||||
#endif
|
||||
@@ -1,36 +0,0 @@
|
||||
#include "fastfetch.h"
|
||||
#include "common/processing.h"
|
||||
#include "common/io.h"
|
||||
|
||||
#include <stdlib.h>
|
||||
#include <unistd.h>
|
||||
#include <sys/wait.h>
|
||||
|
||||
void ffProcessAppendStdOut(FFstrbuf* buffer, char* const argv[])
|
||||
{
|
||||
int pipes[2];
|
||||
|
||||
if(pipe(pipes) == -1)
|
||||
return;
|
||||
|
||||
pid_t childPid = fork();
|
||||
if(childPid == -1)
|
||||
return;
|
||||
|
||||
//Child
|
||||
if(childPid == 0)
|
||||
{
|
||||
dup2(pipes[1], STDOUT_FILENO);
|
||||
close(pipes[0]);
|
||||
close(pipes[1]);
|
||||
close(STDERR_FILENO);
|
||||
execvp(argv[0], argv);
|
||||
exit(901);
|
||||
}
|
||||
|
||||
//Parent
|
||||
close(pipes[1]);
|
||||
waitpid(childPid, NULL, 0);
|
||||
ffAppendFDBuffer(pipes[0], buffer);
|
||||
close(pipes[0]);
|
||||
}
|
||||
@@ -1,10 +0,0 @@
|
||||
#pragma once
|
||||
|
||||
#ifndef FF_INCLUDED_common_processing
|
||||
#define FF_INCLUDED_common_processing
|
||||
|
||||
#include "util/FFstrbuf.h"
|
||||
|
||||
void ffProcessAppendStdOut(FFstrbuf* buffer, char* const argv[]);
|
||||
|
||||
#endif
|
||||
@@ -1,207 +0,0 @@
|
||||
#include "fastfetch.h"
|
||||
#include "common/properties.h"
|
||||
|
||||
#include <stdlib.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
|
||||
|
||||
bool ffParsePropFileValues(const char* filename, uint32_t numQueries, FFpropquery* queries)
|
||||
{
|
||||
FILE* file = fopen(filename, "r");
|
||||
if(file == NULL)
|
||||
return false;
|
||||
|
||||
bool valueStorage[4];
|
||||
bool* unsetValues;
|
||||
|
||||
if(numQueries > sizeof(valueStorage) / sizeof(valueStorage[0]))
|
||||
unsetValues = malloc(sizeof(bool) * numQueries);
|
||||
else
|
||||
unsetValues = valueStorage;
|
||||
|
||||
bool allSet = true;
|
||||
for(uint32_t i = 0; i < numQueries; i++)
|
||||
{
|
||||
if((unsetValues[i] = queries[i].buffer->length == 0))
|
||||
allSet = false;
|
||||
}
|
||||
|
||||
if(allSet)
|
||||
goto done;
|
||||
|
||||
char* line = NULL;
|
||||
size_t len = 0;
|
||||
|
||||
while (getline(&line, &len, file) != -1)
|
||||
{
|
||||
for(uint32_t i = 0; i < numQueries; i++)
|
||||
{
|
||||
if(!unsetValues[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;
|
||||
}
|
||||
}
|
||||
|
||||
if(line != NULL)
|
||||
free(line);
|
||||
|
||||
done:
|
||||
fclose(file);
|
||||
if(unsetValues != valueStorage)
|
||||
free(unsetValues);
|
||||
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;
|
||||
|
||||
FFstrbuf baseDir;
|
||||
ffStrbufInitA(&baseDir, 64);
|
||||
|
||||
for(uint32_t i = 0; i < instance->state.configDirs.length; i++)
|
||||
{
|
||||
//We need to copy the config dir each time, because it used by multiple threads, so we can't directly write to it.
|
||||
ffStrbufSet(&baseDir, ffListGet(&instance->state.configDirs, i));
|
||||
ffStrbufAppendS(&baseDir, relativeFile);
|
||||
|
||||
if(ffParsePropFileValues(baseDir.chars, numQueries, queries))
|
||||
foundAFile = true;
|
||||
|
||||
bool allSet = true;
|
||||
for(uint32_t k = 0; k < numQueries; k++)
|
||||
{
|
||||
if(queries[k].buffer->length == 0)
|
||||
{
|
||||
allSet = false;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if(allSet)
|
||||
break;
|
||||
}
|
||||
|
||||
ffStrbufDestroy(&baseDir);
|
||||
|
||||
return foundAFile;
|
||||
}
|
||||
|
||||
bool ffParsePropFileConfig(const FFinstance* instance, const char* relativeFile, const char* start, FFstrbuf* buffer)
|
||||
{
|
||||
return ffParsePropFileConfigValues(instance, relativeFile, 1, (FFpropquery[]){{start, buffer}});
|
||||
}
|
||||
@@ -1,23 +0,0 @@
|
||||
#pragma once
|
||||
|
||||
#ifndef FF_INCLUDED_common_properties
|
||||
#define FF_INCLUDED_common_properties
|
||||
|
||||
#include "util/FFstrbuf.h"
|
||||
|
||||
typedef struct FFpropquery
|
||||
{
|
||||
const char* start;
|
||||
FFstrbuf* buffer;
|
||||
} FFpropquery;
|
||||
|
||||
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);
|
||||
bool ffParsePropFileHome(const FFinstance* instance, const char* relativeFile, const char* start, FFstrbuf* buffer);
|
||||
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);
|
||||
|
||||
#endif
|
||||
@@ -1,361 +0,0 @@
|
||||
#include "fastfetch.h"
|
||||
#include "common/settings.h"
|
||||
#include "common/library.h"
|
||||
#include "common/io.h"
|
||||
|
||||
#include <pthread.h>
|
||||
#include <string.h>
|
||||
|
||||
typedef enum FFInitState
|
||||
{
|
||||
FF_INITSTATE_UNINITIALIZED = 0,
|
||||
FF_INITSTATE_SUCCESSFUL = 1,
|
||||
FF_INITSTATE_FAILED = 2
|
||||
} FFInitState;
|
||||
|
||||
#define FF_LIBRARY_DATA_LOAD_INIT(dataObject, userLibraryName, ...) \
|
||||
static dataObject data; \
|
||||
static pthread_mutex_t mutex = PTHREAD_MUTEX_INITIALIZER; \
|
||||
static FFInitState initState = FF_INITSTATE_UNINITIALIZED; \
|
||||
pthread_mutex_lock(&mutex); \
|
||||
if(initState != FF_INITSTATE_UNINITIALIZED) {\
|
||||
pthread_mutex_unlock(&mutex); \
|
||||
return initState == FF_INITSTATE_SUCCESSFUL ? &data : NULL; \
|
||||
} \
|
||||
initState = FF_INITSTATE_SUCCESSFUL; \
|
||||
void* libraryHandle = ffLibraryLoad(&userLibraryName, __VA_ARGS__, NULL); \
|
||||
if(libraryHandle == NULL) { \
|
||||
initState = FF_INITSTATE_FAILED; \
|
||||
pthread_mutex_unlock(&mutex); \
|
||||
return NULL; \
|
||||
} \
|
||||
|
||||
#define FF_LIBRARY_DATA_LOAD_SYMBOL(symbolName) \
|
||||
data.ff ## symbolName = dlsym(libraryHandle, #symbolName); \
|
||||
if(data.ff ## symbolName == NULL) { \
|
||||
dlclose(libraryHandle); \
|
||||
initState = FF_INITSTATE_FAILED; \
|
||||
pthread_mutex_unlock(&mutex); \
|
||||
return NULL; \
|
||||
}
|
||||
|
||||
#define FF_LIBRARY_DATA_LOAD_RETURN \
|
||||
initState = FF_INITSTATE_SUCCESSFUL; \
|
||||
pthread_mutex_unlock(&mutex); \
|
||||
return &data;
|
||||
|
||||
#define FF_LIBRARY_DATA_LOAD_ERROR \
|
||||
{ \
|
||||
dlclose(libraryHandle); \
|
||||
initState = FF_INITSTATE_FAILED; \
|
||||
pthread_mutex_unlock(&mutex); \
|
||||
return NULL; \
|
||||
}
|
||||
|
||||
#ifdef FF_HAVE_GIO
|
||||
#include <gio/gio.h>
|
||||
|
||||
typedef struct GVariantGetters
|
||||
{
|
||||
FF_LIBRARY_SYMBOL(g_variant_get_string)
|
||||
FF_LIBRARY_SYMBOL(g_variant_get_boolean)
|
||||
FF_LIBRARY_SYMBOL(g_variant_get_int32)
|
||||
} GVariantGetters;
|
||||
|
||||
static FFvariant getGVariantValue(GVariant* variant, FFvarianttype type, const GVariantGetters* variantGetters)
|
||||
{
|
||||
if(variant == NULL)
|
||||
return FF_VARIANT_NULL;
|
||||
else if(type == FF_VARIANT_TYPE_STRING)
|
||||
return (FFvariant) {.strValue = variantGetters->ffg_variant_get_string(variant, NULL)};
|
||||
else if(type == FF_VARIANT_TYPE_BOOL)
|
||||
return (FFvariant) {.boolValue = (bool) variantGetters->ffg_variant_get_boolean(variant), .boolValueSet = true};
|
||||
else if(type == FF_VARIANT_TYPE_INT)
|
||||
return (FFvariant) {.intValue = variantGetters->ffg_variant_get_int32(variant)};
|
||||
else
|
||||
return FF_VARIANT_NULL;
|
||||
}
|
||||
|
||||
typedef struct GSettingsData
|
||||
{
|
||||
FF_LIBRARY_SYMBOL(g_settings_schema_source_lookup)
|
||||
FF_LIBRARY_SYMBOL(g_settings_schema_has_key)
|
||||
FF_LIBRARY_SYMBOL(g_settings_new_full)
|
||||
FF_LIBRARY_SYMBOL(g_settings_get_value)
|
||||
FF_LIBRARY_SYMBOL(g_settings_get_user_value)
|
||||
FF_LIBRARY_SYMBOL(g_settings_get_default_value)
|
||||
FF_LIBRARY_SYMBOL(g_settings_schema_source_get_default)
|
||||
GSettingsSchemaSource* schemaSource;
|
||||
GVariantGetters variantGetters;
|
||||
} GSettingsData;
|
||||
|
||||
static const GSettingsData* getGSettingsData(FFinstance* instance)
|
||||
{
|
||||
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)
|
||||
FF_LIBRARY_DATA_LOAD_SYMBOL(g_settings_new_full)
|
||||
FF_LIBRARY_DATA_LOAD_SYMBOL(g_settings_get_value)
|
||||
FF_LIBRARY_DATA_LOAD_SYMBOL(g_settings_get_user_value)
|
||||
FF_LIBRARY_DATA_LOAD_SYMBOL(g_settings_get_default_value)
|
||||
FF_LIBRARY_DATA_LOAD_SYMBOL(g_settings_schema_source_get_default)
|
||||
|
||||
#define data data.variantGetters
|
||||
FF_LIBRARY_DATA_LOAD_SYMBOL(g_variant_get_string)
|
||||
FF_LIBRARY_DATA_LOAD_SYMBOL(g_variant_get_boolean)
|
||||
FF_LIBRARY_DATA_LOAD_SYMBOL(g_variant_get_int32)
|
||||
#undef data
|
||||
|
||||
data.schemaSource = data.ffg_settings_schema_source_get_default();
|
||||
if(data.schemaSource == NULL)
|
||||
FF_LIBRARY_DATA_LOAD_ERROR
|
||||
|
||||
FF_LIBRARY_DATA_LOAD_RETURN
|
||||
}
|
||||
|
||||
FFvariant ffSettingsGetGSettings(FFinstance* instance, const char* schemaName, const char* path, const char* key, FFvarianttype type)
|
||||
{
|
||||
const GSettingsData* data = getGSettingsData(instance);
|
||||
if(data == NULL)
|
||||
return FF_VARIANT_NULL;
|
||||
|
||||
GSettingsSchema* schema = data->ffg_settings_schema_source_lookup(data->schemaSource, schemaName, true);
|
||||
if(schema == NULL)
|
||||
return FF_VARIANT_NULL;
|
||||
|
||||
if(data->ffg_settings_schema_has_key(schema, key) == false)
|
||||
return FF_VARIANT_NULL;
|
||||
|
||||
GSettings* settings = data->ffg_settings_new_full(schema, NULL, path);
|
||||
if(settings == NULL)
|
||||
return FF_VARIANT_NULL;
|
||||
|
||||
GVariant* variant = data->ffg_settings_get_value(settings, key);
|
||||
if(variant != NULL)
|
||||
return getGVariantValue(variant, type, &data->variantGetters);
|
||||
|
||||
variant = data->ffg_settings_get_user_value(settings, key);
|
||||
if(variant != NULL)
|
||||
return getGVariantValue(variant, type, &data->variantGetters);
|
||||
|
||||
variant = data->ffg_settings_get_default_value(settings, key);
|
||||
return getGVariantValue(variant, type, &data->variantGetters);
|
||||
}
|
||||
#else //FF_HAVE_GIO
|
||||
FFvariant ffSettingsGetGSettings(FFinstance* instance, const char* schemaName, const char* path, const char* key, FFvarianttype type)
|
||||
{
|
||||
FF_UNUSED(instance, schemaName, path, key, type)
|
||||
return FF_VARIANT_NULL;
|
||||
}
|
||||
#endif //FF_HAVE_GIO
|
||||
|
||||
#ifdef FF_HAVE_DCONF
|
||||
#include <dconf.h>
|
||||
|
||||
typedef struct DConfData
|
||||
{
|
||||
FF_LIBRARY_SYMBOL(dconf_client_read_full)
|
||||
FF_LIBRARY_SYMBOL(dconf_client_new)
|
||||
GVariantGetters variantGetters;
|
||||
DConfClient* client;
|
||||
} DConfData;
|
||||
|
||||
static const DConfData* getDConfData(FFinstance* instance)
|
||||
{
|
||||
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)
|
||||
|
||||
#define data data.variantGetters
|
||||
FF_LIBRARY_DATA_LOAD_SYMBOL(g_variant_get_string)
|
||||
FF_LIBRARY_DATA_LOAD_SYMBOL(g_variant_get_boolean)
|
||||
FF_LIBRARY_DATA_LOAD_SYMBOL(g_variant_get_int32)
|
||||
#undef data
|
||||
|
||||
data.client = data.ffdconf_client_new();
|
||||
if(data.client == NULL)
|
||||
FF_LIBRARY_DATA_LOAD_ERROR
|
||||
|
||||
FF_LIBRARY_DATA_LOAD_RETURN
|
||||
}
|
||||
|
||||
FFvariant ffSettingsGetDConf(FFinstance* instance, const char* key, FFvarianttype type)
|
||||
{
|
||||
const DConfData* data = getDConfData(instance);
|
||||
if(data == NULL)
|
||||
return FF_VARIANT_NULL;
|
||||
|
||||
GVariant* variant = data->ffdconf_client_read_full(data->client, key, DCONF_READ_FLAGS_NONE, NULL);
|
||||
if(variant != NULL)
|
||||
return getGVariantValue(variant, type, &data->variantGetters);
|
||||
|
||||
variant = data->ffdconf_client_read_full(data->client, key, DCONF_READ_USER_VALUE, NULL);
|
||||
if(variant != NULL)
|
||||
return getGVariantValue(variant, type, &data->variantGetters);
|
||||
|
||||
variant = data->ffdconf_client_read_full(data->client, key, DCONF_READ_DEFAULT_VALUE, NULL);
|
||||
return getGVariantValue(variant, type, &data->variantGetters);
|
||||
}
|
||||
#else //FF_HAVE_DCONF
|
||||
FFvariant ffSettingsGetDConf(FFinstance* instance, const char* key, FFvarianttype type)
|
||||
{
|
||||
FF_UNUSED(instance, key, type)
|
||||
return FF_VARIANT_NULL;
|
||||
}
|
||||
#endif //FF_HAVE_DCONF
|
||||
|
||||
FFvariant ffSettingsGet(FFinstance* instance, const char* dconfKey, const char* gsettingsSchemaName, const char* gsettingsPath, const char* gsettingsKey, FFvarianttype type)
|
||||
{
|
||||
FFvariant gsettings = ffSettingsGetGSettings(instance, gsettingsSchemaName, gsettingsPath, gsettingsKey, type);
|
||||
|
||||
if(
|
||||
(type == FF_VARIANT_TYPE_BOOL && gsettings.boolValueSet) ||
|
||||
(type != FF_VARIANT_TYPE_BOOL && gsettings.strValue != NULL)
|
||||
) return gsettings;
|
||||
|
||||
return ffSettingsGetDConf(instance, dconfKey, type);
|
||||
}
|
||||
|
||||
#ifdef FF_HAVE_XFCONF
|
||||
#include <xfconf/xfconf.h>
|
||||
|
||||
typedef struct XFConfData
|
||||
{
|
||||
FF_LIBRARY_SYMBOL(xfconf_channel_get)
|
||||
FF_LIBRARY_SYMBOL(xfconf_channel_has_property)
|
||||
FF_LIBRARY_SYMBOL(xfconf_channel_get_string)
|
||||
FF_LIBRARY_SYMBOL(xfconf_channel_get_bool)
|
||||
FF_LIBRARY_SYMBOL(xfconf_channel_get_int)
|
||||
FF_LIBRARY_SYMBOL(xfconf_init)
|
||||
} XFConfData;
|
||||
|
||||
static const XFConfData* getXFConfData(FFinstance* instance)
|
||||
{
|
||||
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)
|
||||
FF_LIBRARY_DATA_LOAD_SYMBOL(xfconf_channel_get_string)
|
||||
FF_LIBRARY_DATA_LOAD_SYMBOL(xfconf_channel_get_bool)
|
||||
FF_LIBRARY_DATA_LOAD_SYMBOL(xfconf_channel_get_int)
|
||||
FF_LIBRARY_DATA_LOAD_SYMBOL(xfconf_init)
|
||||
|
||||
if(data.ffxfconf_init(NULL) == false)
|
||||
FF_LIBRARY_DATA_LOAD_ERROR
|
||||
|
||||
FF_LIBRARY_DATA_LOAD_RETURN
|
||||
}
|
||||
|
||||
FFvariant ffSettingsGetXFConf(FFinstance* instance, const char* channelName, const char* propertyName, FFvarianttype type)
|
||||
{
|
||||
const XFConfData* data = getXFConfData(instance);
|
||||
if(data == NULL)
|
||||
return FF_VARIANT_NULL;
|
||||
|
||||
XfconfChannel* channel = data->ffxfconf_channel_get(channelName); // Never fails according to documentation but rather returns an empty channel
|
||||
|
||||
if(!data->ffxfconf_channel_has_property(channel, propertyName))
|
||||
return FF_VARIANT_NULL;
|
||||
|
||||
if(type == FF_VARIANT_TYPE_INT)
|
||||
return (FFvariant) {.intValue = data->ffxfconf_channel_get_int(channel, propertyName, 0)};
|
||||
|
||||
if(type == FF_VARIANT_TYPE_STRING)
|
||||
return (FFvariant) {.strValue = data->ffxfconf_channel_get_string(channel, propertyName, NULL)};
|
||||
|
||||
if(type == FF_VARIANT_TYPE_BOOL)
|
||||
return (FFvariant) {.boolValue = data->ffxfconf_channel_get_bool(channel, propertyName, false), .boolValueSet = true};
|
||||
|
||||
return FF_VARIANT_NULL;
|
||||
}
|
||||
#else //FF_HAVE_XFCONF
|
||||
FFvariant ffSettingsGetXFConf(FFinstance* instance, const char* channelName, const char* propertyName, FFvarianttype type)
|
||||
{
|
||||
FF_UNUSED(instance, channelName, propertyName, type)
|
||||
return FF_VARIANT_NULL;
|
||||
}
|
||||
#endif //FF_HAVE_XFCONF
|
||||
|
||||
#ifdef FF_HAVE_SQLITE3
|
||||
#include <sqlite3.h>
|
||||
#include <sys/stat.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(const FFinstance* instance, const char* dbPath, const char* query)
|
||||
{
|
||||
if(!ffFileExists(dbPath, S_IFREG))
|
||||
return 0;
|
||||
|
||||
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(const 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) {
|
||||
uint32_t originalLength = result->length;
|
||||
ffStrbufEnsureFree(result, PROP_VALUE_MAX);
|
||||
result->length += (uint32_t) __system_property_get(propName, result->chars + result->length);
|
||||
return result->length > originalLength;
|
||||
}
|
||||
#endif
|
||||
@@ -1,39 +0,0 @@
|
||||
#pragma once
|
||||
|
||||
#ifndef FF_INCLUDED_common_settings
|
||||
#define FF_INCLUDED_common_settings
|
||||
|
||||
#include "fastfetch.h"
|
||||
|
||||
typedef enum FFvarianttype
|
||||
{
|
||||
FF_VARIANT_TYPE_STRING,
|
||||
FF_VARIANT_TYPE_BOOL,
|
||||
FF_VARIANT_TYPE_INT
|
||||
} FFvarianttype;
|
||||
|
||||
typedef union FFvariant
|
||||
{
|
||||
const char* strValue;
|
||||
int32_t intValue;
|
||||
struct
|
||||
{
|
||||
bool boolValueSet;
|
||||
bool boolValue;
|
||||
};
|
||||
} FFvariant;
|
||||
|
||||
#define FF_VARIANT_NULL ((FFvariant){.strValue = NULL})
|
||||
|
||||
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(const FFinstance* instance, const char* dbPath, const char* query);
|
||||
|
||||
#ifdef __ANDROID__
|
||||
bool ffSettingsGetAndroidProperty(const char* propName, FFstrbuf* result);
|
||||
#endif
|
||||
|
||||
#endif
|
||||
@@ -1,16 +0,0 @@
|
||||
# Fastfetch system configuration
|
||||
# This file sets the defaults for all users of the system.
|
||||
|
||||
# For more information about configuration files work,
|
||||
# see the file ~/.config/fastfetch/config.conf, generated during first
|
||||
# All options that can be put there (or passed as argument) can be put here too.
|
||||
|
||||
# This file was shipped with $"FASTFETCH_PROJECT_VERSION$".
|
||||
# Use fastfetch --print-config-system > /etc/fastfetch/config.conf to overwrite this file with the current defaults
|
||||
|
||||
# User config option
|
||||
# Sets if fastfetch should load user provided configuration.
|
||||
# If this is off, fastfetch ignores the user config file and any passed arguments
|
||||
# Must be true or false
|
||||
# Default is true.
|
||||
#--load-user-config true
|
||||
@@ -1,306 +0,0 @@
|
||||
# Fastfetch configuration
|
||||
# Write every argument in different lines.
|
||||
# Direct arguments will overwrite the corresponding ones in this file.
|
||||
# Argument keys are not case sensitive.
|
||||
# Whitespaces are trimmed at the beginning and the end.
|
||||
# Empty lines or lines starting with # are ignored.
|
||||
|
||||
# This file was shipped with $"FASTFETCH_PROJECT_VERSION$".
|
||||
# Use fastfetch --print-config-user > ~/.config/fastfetch/config.conf to overwrite this file with the current defaults
|
||||
|
||||
# Below some often usefull options are listed. Uncomment and modify them so they take affect.
|
||||
# Note that there are a lot more options than the ones listed here, take a look at "fastfetch --help".
|
||||
# Of course all of them can be made persistent here too.
|
||||
|
||||
# Config option:
|
||||
# Load additional config files.
|
||||
# Some are shipped with fastfetch, list them with "fastfetch --list-presets".
|
||||
# Must be a path to a config file or the name of a shipped preset.
|
||||
# The config file is completly loaded before continuing in the current file, so the placement of this option matters, as later options overwrite already set ones.
|
||||
# Can be used multiple times to load multiple config files / presets.
|
||||
#--load-config /path/to/config.txt
|
||||
|
||||
# Structure option:
|
||||
# Sets the modules to use and their order.
|
||||
# Must be a list of module names, separated by colons.
|
||||
# List available modules with "fastfetch --list-modules".
|
||||
# Get the default structure with "fastfetch --print-structure".
|
||||
#--structure $"FASTFETCH_DATATEXT_STRUCTURE$"
|
||||
|
||||
# Multithreading option:
|
||||
# Sets if fastfetch should use multiple threads to detect the values.
|
||||
# Must be true or false.
|
||||
# Default is true.
|
||||
#--multithreading true
|
||||
|
||||
# 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.
|
||||
# Must be true or false.
|
||||
# Default is true.
|
||||
#--disable-linewrap true
|
||||
|
||||
# Cursor option:
|
||||
# Sets if fastfetch should hide the console cursor during the run.
|
||||
# Must be true or false.
|
||||
# Default is true.
|
||||
#--hide-cursor 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
|
||||
|
||||
# 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
|
||||
|
||||
# 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
|
||||
|
||||
# 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.
|
||||
# In the user logo, they replace $[1-9]. Use $$ to print a single $ sign.
|
||||
# Must be linux console color codes or the name of a color.
|
||||
# Default is the one specified by the logo.
|
||||
# Use "fastfetch --help color" to learn more and see examples.
|
||||
# Valid index range is [1-9].
|
||||
#--logo-color-1 red
|
||||
#--logo-color-2 32
|
||||
# [...]
|
||||
#--logo-color-9 yellow
|
||||
|
||||
# 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.
|
||||
#--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
|
||||
|
||||
# Title FQDN option:
|
||||
# Sets if the title should use the fully qualified domain name.
|
||||
# Must be true or false.
|
||||
# Default is false.
|
||||
#--title-fqdn false
|
||||
|
||||
# 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.
|
||||
# Must be a positive integer.
|
||||
# Default is 0 (disabled).
|
||||
#--public-ip-timeout 0
|
||||
|
||||
# OS file option
|
||||
# Sets the path to the file containing the operating system information.
|
||||
# Should be a valid path to an existing file.
|
||||
# Note that you might need to run fastfetch with --recache once for it to take affect.
|
||||
# Default is /etc/os-release.
|
||||
#--os-file /etc/os-release
|
||||
|
||||
# Player name option
|
||||
# Sets the name of the player. This is also used in song detection
|
||||
# Must be the exact name of the player or a dbus address (e.g. org.mpris.MediaPlayer2.spotify)
|
||||
# Default is the first match starting with org.mpris.MediaPlayer2.
|
||||
#--player-name spotify
|
||||
|
||||
# Escape bedrock option
|
||||
# Sets if fastfetch should escape the bedrock jail, if it detectes that it is running in one
|
||||
# Must be true or false.
|
||||
# Default is true.
|
||||
#--escape-bedrock true
|
||||
|
||||
# GL option
|
||||
# Sets with opengl context creation library to use
|
||||
# Must be either auto, egl, glx or osmesa
|
||||
# Default is auto.
|
||||
#--gl auto
|
||||
|
||||
# Key options:
|
||||
# Sets the displayed key of a module
|
||||
# Can be any string. Some of theme take an argument like a format string. See "fastfetch --help format" for help.
|
||||
#--os-key OS
|
||||
#--host-key Host
|
||||
#--kernel-key Kernel
|
||||
#--uptime-key Uptime
|
||||
#--processes-key Processes
|
||||
#--packages-key Packages
|
||||
#--shell-key Shell
|
||||
#--resolution-key Resolution {1}
|
||||
#--de-key DE
|
||||
#--wm-key WM
|
||||
#--wm-theme-key WM Theme
|
||||
#--theme-key Theme
|
||||
#--icons-key Icons
|
||||
#--font-key Font
|
||||
#--cursor-key Cursor
|
||||
#--terminal-key Terminal
|
||||
#--terminal-font-key Terminal Font
|
||||
#--cpu-key CPU
|
||||
#--cpu-usage-key CPU Usage
|
||||
#--gpu-key GPU {1}
|
||||
#--memory-key Memory
|
||||
#--disk-key Disk ({1})
|
||||
#--battery-key Battery {1}
|
||||
#--locale-key Locale
|
||||
#--local-ip-key Local IP ({1})
|
||||
#--public-ip-key Public IP
|
||||
#--player-key Media Player
|
||||
#--song-key Song
|
||||
#--datetime-key Date Time
|
||||
#--vulkan-key Vulkan
|
||||
#--opengl-key OpenGL
|
||||
#--opencl-key OpenCL
|
||||
|
||||
# Format options:
|
||||
# Sets the format string for module values.
|
||||
# For information on format strings, see "fastfetch --help format".
|
||||
# To see the parameter they take and their default value, see "fastfetch --help *-format", e.g. "fastfetch --help os-format".
|
||||
# An empty format string (As they are currently below) will behave as if it was not set.
|
||||
#--os-format
|
||||
#--host-format
|
||||
#--kernel-format
|
||||
#--uptime-format
|
||||
#--processes-format
|
||||
#--packages-format
|
||||
#--shell-format
|
||||
#--resolution-format
|
||||
#--de-format
|
||||
#--wm-format
|
||||
#--wm-theme-format
|
||||
#--theme-format
|
||||
#--icons-format
|
||||
#--font-format
|
||||
#--cursor-format
|
||||
#--terminal-format
|
||||
#--terminal-font-format
|
||||
#--cpu-format
|
||||
#--cpu-usage-format
|
||||
#--gpu-format
|
||||
#--memory-format
|
||||
#--disk-format
|
||||
#--battery-format
|
||||
#--locale-format
|
||||
#--local-ip-format
|
||||
#--public-ip-format
|
||||
#--player-format
|
||||
#--song-format
|
||||
#--datetime-format
|
||||
#--vulkan-format
|
||||
#--opengl-format
|
||||
#--opencl-format
|
||||
|
||||
# Error options:
|
||||
# Sets the format string to use if an error occured
|
||||
# For information on format strings, see "fastfetch --help format".
|
||||
# Each of them take the error as first and only argument.
|
||||
# If one of them is set, the module will appear, even if --show-errors is not given.
|
||||
#--os-error
|
||||
#--host-error
|
||||
#--kernel-error
|
||||
#--uptime-error
|
||||
#--processes-error
|
||||
#--packages-error
|
||||
#--shell-error
|
||||
#--resolution-error
|
||||
#--de-error
|
||||
#--wm-error
|
||||
#--wm-theme-error
|
||||
#--theme-error
|
||||
#--icons-error
|
||||
#--font-error
|
||||
#--cursor-error
|
||||
#--terminal-error
|
||||
#--terminal-font-error
|
||||
#--cpu-error
|
||||
#--cpu-usage-error
|
||||
#--gpu-error
|
||||
#--memory-error
|
||||
#--disk-error
|
||||
#--battery-error
|
||||
#--locale-error
|
||||
#--local-ip-error
|
||||
#--public-ip-error
|
||||
#--player-error
|
||||
#--song-error
|
||||
#--datetime-error
|
||||
#--vulkan-error
|
||||
#--opengl-error
|
||||
#--opencl-error
|
||||
|
||||
# Library options:
|
||||
# Sets an user specific path to a library to load.
|
||||
# Must be a valid path to a library.
|
||||
#--lib-PCI /usr/lib/libpci.so
|
||||
#--lib-vulkan /usr/lib/libvulkan.so
|
||||
#--lib-wayland /usr/lib/libwayland-client.so
|
||||
#--lib-xcb-randr /usr/lib/libxcb-randr.so
|
||||
#--lib-xcb /usr/lib/libxcb.so
|
||||
#--lib-Xrandr /usr/lib/libXrandr.so
|
||||
#--lib-X11 /usr/lib/libX11.so
|
||||
#--lib-gio /usr/lib/libgio-2.0.so
|
||||
#--lib-DConf /usr/lib/libdconf.so
|
||||
#--lib-DBus /usr/lib/libdbus-1.so
|
||||
#--lib-XFConf /usr/lib/libxfconf-0.so
|
||||
#--lib-sqlite3 /usr/lib/libsqlite3.so
|
||||
#--lib-rpm /usr/lib/librpm.so
|
||||
#--lib-imagemagick /usr/lib/libMagickCore-7.Q16HDRI.so
|
||||
#--lib-z /usr/lib/libz.so
|
||||
#--lib-chafa /usr/lib/libchafa.so
|
||||
#--lib-egl /usr/lib/libEGL.so
|
||||
#--lib-glx /usr/lib/libGLX.so
|
||||
#--lib-osmesa /usr/lib/libOSMesa.so
|
||||
#--lib-opencl /usr/lib/libOpenCL.so
|
||||
@@ -1,100 +0,0 @@
|
||||
Usage: fastfetch <options>
|
||||
|
||||
Informative options:
|
||||
-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-system: prints the default system config and exits
|
||||
--print-config-user: prints the default user config and exits
|
||||
--print-structure: prints the default stucture and exits
|
||||
|
||||
General options:
|
||||
-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
|
||||
--escape-bedrock <?value>: on bedrock linux, sets if it should escape the bedrock jail or not
|
||||
--pipe <?value>: disable logo and all escape sequences
|
||||
|
||||
Logo options:
|
||||
-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
|
||||
|
||||
General module options:
|
||||
--<module>-format <format>: Sets the format string to use for each specific module.
|
||||
To see how a format string works, use fastfetch --help format.
|
||||
To see help about a specific format string, use fastfetch --help <module>-format.
|
||||
|
||||
--<module>-key <key>: Sets the key to use for each specific module.
|
||||
For modules which print multiple lines, the string is parsed as a format string with the index as first character.
|
||||
|
||||
--<module>-error <format>: Sets the error format string to use for each specific module.
|
||||
The error is given as the first and only argument.
|
||||
Setting this for a module will cause it to appear, even if --show-errors is not given.
|
||||
|
||||
Library options: Set the path of a library to load
|
||||
--lib-PCI <path>
|
||||
--lib-vulkan <path>
|
||||
--lib-wayland <path>
|
||||
--lib-xcb-randr <path>
|
||||
--lib-xcb <path>
|
||||
--lib-Xrandr <path>
|
||||
--lib-X11 <path>
|
||||
--lib-gio <path>
|
||||
--lib-DConf <path>
|
||||
--lib-DBus <path>
|
||||
--lib-XFConf <path>
|
||||
--lib-sqlite3 <path>
|
||||
--lib-rpm <path>
|
||||
--lib-imagemagick <path>
|
||||
--lib-z <path>
|
||||
--lib-chafa <path>
|
||||
--lib-egl <path>
|
||||
--lib-glx <path>
|
||||
--lib-osmesa <path>
|
||||
--lib-opencl <path>
|
||||
|
||||
Module specific options:
|
||||
--title-fqdn <?value>: sets if the title should use fully qualified domain name. Default is false.
|
||||
--separator-string <str>: Set the string printed by the separator module
|
||||
--os-file <path>: Set the path to the file containing OS informations
|
||||
--disk-folders <folders>: A colon separated list of folder paths for the disk output. Default is "/:/home"
|
||||
--battery-dir <folder>: The directory where the battery folders are. Standard: /sys/class/power_supply/
|
||||
--localip-show-ipv4 <?value>: Show ipv4 addresses in local ip module. Default is true
|
||||
--localip-show-ipv6 <?value>: Show ipv6 addresses in local ip module. Default is false
|
||||
--localip-show-loop <?value>: Show loop back addresses (127.0.0.1) in local ip module. Default is false
|
||||
--public-ip-timeout: Time in milliseconds to wait for the public ip server to respond. Default is disabled (0)
|
||||
--player-name: The name of the player to use
|
||||
--gl <value>: Sets the opengl context creation library to use. Must be auto, egl, glx or osmesa. Default is auto
|
||||
|
||||
Parsing is not case sensitive. E.g. "--lib-PCI" is equal to "--Lib-Pci"
|
||||
If a value starts with a ?, it is optional. "true" will be used if not set.
|
||||
A (+) at the end indicates that more help can be printed with --help <option>
|
||||
All options can be made permanent in $XDG_CONFIG_HOME/fastfetch/config.conf
|
||||
@@ -1,9 +0,0 @@
|
||||
usage: fastfetch --color <color>
|
||||
|
||||
<color> must be a color encoding for linux terminals. It is inserted between "ESC[" and "m".
|
||||
Infos about them can be found here: https://en.wikipedia.org/wiki/ANSI_escape_code#SGR_(Select_Graphic_Rendition)_parameters.
|
||||
Examples:
|
||||
--color 35: sets the color to pink
|
||||
--color 4;92: sets the color to bright Green with underline
|
||||
--color 5;104: blinking text on a blue background
|
||||
If no color is set, the main color of the logo will be used.
|
||||
@@ -1,9 +0,0 @@
|
||||
usage: fastfetch --load-config <file>
|
||||
|
||||
Loads a config file. A config file contains one flag per line. Empty lines or lines starting with # are ignored.
|
||||
If the file is relative it looks in the following order:
|
||||
- relative to the current working directory
|
||||
- relative to ~/.local/share/fastfetch/presets/
|
||||
- relative to /usr/share/fastfetch/presets/
|
||||
Fastfetch provides some default presets. List them with --print-available-presets.
|
||||
Note that this will only print presets fastfetch knows about and not every possible one.
|
||||
@@ -1,36 +0,0 @@
|
||||
A format string is a string that contains placeholders for values.
|
||||
These placeholders begin with a '{', containing the index of the value, and end with a '}'.
|
||||
For example the format string "Values: {1} ({2})", with the values "First" and "My second val", will produce
|
||||
The format string can contain placeholders in any order and have multiple occurences.
|
||||
To include spaces when setting from the command line, surround the whole string with double quotes (").
|
||||
|
||||
If the value index is missing, meaning the placeholder is "{}", an internal counter sets the value index.
|
||||
This means that the format string "Values: {1} ({2})" is equivalent to "Values: {} ({})".
|
||||
Note that this counter only counts empty placeholders, so the format string "{2} {} {}" will contain the second v
|
||||
|
||||
To make formatting easier, a double open curly brace ("{{") will be printed as a single open curly brace and not
|
||||
If a value index is misformatted or wants a non-existing value, it will be printed as is, with the curly braces aro
|
||||
If the last placeholder isn't closed, it will be treated like it was at the end of the format string.
|
||||
|
||||
To only print something if a variable is set, use "{?<index>} ... {?}".
|
||||
For example, to only print a second value if it is set, use "{?2} Second value: {2}{?}".
|
||||
If a "{?}" is found without an opener, it is printed as is.
|
||||
|
||||
To only print something if a variable is not set, do the same as with if, just replace every '?' with a '!'.
|
||||
For example to print a fallback for a second value if it is not set, use "{?2}{2}{?}{/2}Second value fallback{/}"
|
||||
|
||||
To stop formatting at any point in the format string, use "{-}".
|
||||
|
||||
To print something with color, start a placeholder with a '#' and then the linux terminal color encoding.
|
||||
"\\033[" at the start, and an 'm' at the end is automatically added, so don't do that.
|
||||
A "{#}" is equivalent to a "{#0}" and resets everything to normal.
|
||||
For example to print something pink and underline, use "{#4;35}...{#}".
|
||||
Information about what the numbers mean can be found here: https://en.wikipedia.org/wiki/ANSI_escape_code#SGR_(Sele
|
||||
Which escape codes are supported and how they look is defined by your terminal.
|
||||
|
||||
If a format string evaluates to an empty value, the whole line in the output will be discarded.
|
||||
You can therefore use --host-format " " to disable host output.
|
||||
Note that --host-format "" would evaluate as not set, and therefore use the built-in host format
|
||||
|
||||
Format string is also the way to go to set a fixed value - just use one without placeholders.
|
||||
For example when running in headless mode, you could use "--resolution-format "Preferred".
|
||||
@@ -1,38 +0,0 @@
|
||||
Battery
|
||||
Break
|
||||
Colors
|
||||
CPU
|
||||
CPUUsage
|
||||
Cursor
|
||||
Date
|
||||
Datetime
|
||||
DE
|
||||
Disk
|
||||
Font
|
||||
GPU
|
||||
Host
|
||||
Icons
|
||||
Kernel
|
||||
Locale
|
||||
LocalIP
|
||||
Memory
|
||||
OS
|
||||
OpenCL
|
||||
OpenGL
|
||||
Packages
|
||||
Player
|
||||
Processes
|
||||
PublicIP
|
||||
Resolution
|
||||
Separator
|
||||
Shell
|
||||
Song
|
||||
Terminal
|
||||
TerminalFont
|
||||
Theme
|
||||
Time
|
||||
Title
|
||||
Uptime
|
||||
Vulkan
|
||||
WM
|
||||
WMTheme
|
||||
@@ -1 +0,0 @@
|
||||
Title:Separator:OS:Host:Kernel:Uptime:Packages:Shell:Resolution:DE:WM:WMTheme:Theme:Icons:Font:Cursor:Terminal:TerminalFont:CPU:GPU:Memory:Disk:Battery:Locale:Break:Colors
|
||||
@@ -1,73 +0,0 @@
|
||||
#include "fastfetch.h"
|
||||
#include "detection/datetime.h"
|
||||
|
||||
#include <time.h>
|
||||
#include <pthread.h>
|
||||
|
||||
const FFDateTimeResult* ffDetectDateTime(const FFinstance* instance)
|
||||
{
|
||||
FF_UNUSED(instance); //We may need it later for additional configuration
|
||||
|
||||
static FFDateTimeResult result;
|
||||
static pthread_mutex_t mutex = PTHREAD_MUTEX_INITIALIZER;
|
||||
static bool init = false;
|
||||
|
||||
pthread_mutex_lock(&mutex);
|
||||
if (init)
|
||||
{
|
||||
pthread_mutex_unlock(&mutex);
|
||||
return &result;
|
||||
}
|
||||
init = true;
|
||||
|
||||
const time_t t = time(NULL);
|
||||
struct tm* tm = localtime(&t);
|
||||
|
||||
result.year = (uint16_t) (tm->tm_year + 1900);
|
||||
result.yearShort = (uint8_t) (result.year % 100);
|
||||
result.month = (uint8_t) (tm->tm_mon + 1);
|
||||
|
||||
ffStrbufInit(&result.monthPretty);
|
||||
result.monthPretty.length = (uint32_t) strftime(result.monthPretty.chars, ffStrbufGetFree(&result.monthPretty), "%m", tm);
|
||||
|
||||
ffStrbufInit(&result.monthName);
|
||||
result.monthName.length = (uint32_t) strftime(result.monthName.chars, ffStrbufGetFree(&result.monthName), "%B", tm);
|
||||
|
||||
ffStrbufInit(&result.monthNameShort);
|
||||
result.monthNameShort.length = (uint32_t) strftime(result.monthNameShort.chars, ffStrbufGetFree(&result.monthNameShort), "%b", tm);
|
||||
|
||||
result.week = (uint8_t) (tm->tm_yday / 7 + 1);
|
||||
|
||||
ffStrbufInit(&result.weekday);
|
||||
result.weekday.length = (uint32_t) strftime(result.weekday.chars, ffStrbufGetFree(&result.weekday), "%A", tm);
|
||||
|
||||
ffStrbufInit(&result.weekdayShort);
|
||||
result.weekdayShort.length = (uint32_t) strftime(result.weekdayShort.chars, ffStrbufGetFree(&result.weekdayShort), "%a", tm);
|
||||
|
||||
result.dayInYear = (uint8_t) (tm->tm_yday + 1);
|
||||
result.dayInMonth = (uint8_t) tm->tm_mday;
|
||||
result.dayInWeek = tm->tm_wday == 0 ? 7 : (uint8_t) tm->tm_wday;
|
||||
|
||||
result.hour = (uint8_t) tm->tm_hour;
|
||||
|
||||
ffStrbufInit(&result.hourPretty);
|
||||
result.hourPretty.length = (uint32_t) strftime(result.hourPretty.chars, ffStrbufGetFree(&result.hourPretty), "%H", tm);
|
||||
|
||||
result.hour12 = (uint8_t) (result.hour % 12);
|
||||
|
||||
ffStrbufInit(&result.hour12Pretty);
|
||||
result.hour12Pretty.length = (uint32_t) strftime(result.hour12Pretty.chars, ffStrbufGetFree(&result.hour12Pretty), "%I", tm);
|
||||
|
||||
result.minute = (uint8_t) tm->tm_min;
|
||||
|
||||
ffStrbufInit(&result.minutePretty);
|
||||
result.minutePretty.length = (uint32_t) strftime(result.minutePretty.chars, ffStrbufGetFree(&result.minutePretty), "%M", tm);
|
||||
|
||||
result.second = (uint8_t) tm->tm_sec;
|
||||
|
||||
ffStrbufInit(&result.secondPretty);
|
||||
result.secondPretty.length = (uint32_t) strftime(result.secondPretty.chars, ffStrbufGetFree(&result.secondPretty), "%S", tm);
|
||||
|
||||
pthread_mutex_unlock(&mutex);
|
||||
return &result;
|
||||
}
|
||||
@@ -1,35 +0,0 @@
|
||||
#pragma once
|
||||
|
||||
#ifndef FF_INCLUDED_detection_datetime
|
||||
#define FF_INCLUDED_detection_datetime
|
||||
|
||||
#include "fastfetch.h"
|
||||
|
||||
typedef struct FFDateTimeResult
|
||||
{
|
||||
//Examples for 21.02.2022 - 15:18:37
|
||||
uint16_t year; //2022
|
||||
uint8_t yearShort; //22
|
||||
uint8_t month; //2
|
||||
FFstrbuf monthPretty; //02
|
||||
FFstrbuf monthName; //February
|
||||
FFstrbuf monthNameShort; //Feb
|
||||
uint8_t week; //8
|
||||
FFstrbuf weekday; //Monday
|
||||
FFstrbuf weekdayShort; //Mon
|
||||
uint16_t dayInYear; //52
|
||||
uint8_t dayInMonth; //21
|
||||
uint8_t dayInWeek; //1
|
||||
uint8_t hour; //15
|
||||
FFstrbuf hourPretty; //15
|
||||
uint8_t hour12; //3
|
||||
FFstrbuf hour12Pretty; //03
|
||||
uint8_t minute; //18
|
||||
FFstrbuf minutePretty; //18
|
||||
uint8_t second; //37
|
||||
FFstrbuf secondPretty; //37
|
||||
} FFDateTimeResult;
|
||||
|
||||
const FFDateTimeResult* ffDetectDateTime(const FFinstance* instance);
|
||||
|
||||
#endif
|
||||
@@ -1,29 +0,0 @@
|
||||
#pragma once
|
||||
|
||||
#ifndef FF_INCLUDED_detection_displayserver
|
||||
#define FF_INCLUDED_detection_displayserver
|
||||
|
||||
#include "fastfetch.h"
|
||||
|
||||
typedef struct FFResolutionResult
|
||||
{
|
||||
uint32_t width;
|
||||
uint32_t height;
|
||||
uint32_t refreshRate;
|
||||
} FFResolutionResult;
|
||||
|
||||
typedef struct FFDisplayServerResult
|
||||
{
|
||||
FFstrbuf wmProcessName;
|
||||
FFstrbuf wmPrettyName;
|
||||
FFstrbuf wmProtocolName;
|
||||
FFstrbuf deProcessName;
|
||||
FFstrbuf dePrettyName;
|
||||
FFstrbuf deVersion;
|
||||
FFlist resolutions; //List of FFResolutionResult
|
||||
} FFDisplayServerResult;
|
||||
|
||||
const FFDisplayServerResult* ffConnectDisplayServer(const FFinstance* instance);
|
||||
|
||||
#endif
|
||||
|
||||
@@ -1,133 +0,0 @@
|
||||
#include "displayServer.h"
|
||||
|
||||
#include <pthread.h>
|
||||
#include <dirent.h>
|
||||
|
||||
uint32_t ffdsParseRefreshRate(int32_t refreshRate)
|
||||
{
|
||||
if(refreshRate <= 0)
|
||||
return 0;
|
||||
|
||||
int remainder = refreshRate % 5;
|
||||
if(remainder >= 3)
|
||||
refreshRate += (5 - remainder);
|
||||
else
|
||||
refreshRate -= remainder;
|
||||
|
||||
//All other typicall refresh rates are dividable by 5
|
||||
if(refreshRate == 145)
|
||||
refreshRate = 144;
|
||||
|
||||
return (uint32_t) refreshRate;
|
||||
}
|
||||
|
||||
bool ffdsAppendResolution(FFDisplayServerResult* result, uint32_t width, uint32_t height, uint32_t refreshRate)
|
||||
{
|
||||
if(width == 0 || height == 0)
|
||||
return false;
|
||||
|
||||
FFResolutionResult* resolution = ffListAdd(&result->resolutions);
|
||||
resolution->width = width;
|
||||
resolution->height = height;
|
||||
resolution->refreshRate = refreshRate;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
static void parseDRM(FFDisplayServerResult* result)
|
||||
{
|
||||
const char* drmDirPath = "/sys/class/drm/";
|
||||
|
||||
DIR* dirp = opendir(drmDirPath);
|
||||
if(dirp == NULL)
|
||||
return;
|
||||
|
||||
FFstrbuf drmDir;
|
||||
ffStrbufInitA(&drmDir, 64);
|
||||
ffStrbufAppendS(&drmDir, drmDirPath);
|
||||
|
||||
uint32_t drmDirLength = drmDir.length;
|
||||
|
||||
struct dirent* entry;
|
||||
while((entry = readdir(dirp)) != NULL)
|
||||
{
|
||||
ffStrbufAppendS(&drmDir, entry->d_name);
|
||||
ffStrbufAppendS(&drmDir, "/modes");
|
||||
|
||||
FILE* modeFile = fopen(drmDir.chars, "r");
|
||||
if(modeFile == NULL)
|
||||
{
|
||||
ffStrbufSubstrBefore(&drmDir, drmDirLength);
|
||||
continue;
|
||||
}
|
||||
|
||||
FFResolutionResult* resolution = ffListAdd(&result->resolutions);
|
||||
resolution->width = 0;
|
||||
resolution->height = 0;
|
||||
resolution->refreshRate = 0;
|
||||
|
||||
int scanned = fscanf(modeFile, "%ix%i", &resolution->width, &resolution->height);
|
||||
if(scanned < 2 || resolution->width == 0 || resolution->height == 0)
|
||||
--result->resolutions.length;
|
||||
|
||||
fclose(modeFile);
|
||||
ffStrbufSubstrBefore(&drmDir, drmDirLength);
|
||||
}
|
||||
|
||||
closedir(dirp);
|
||||
ffStrbufDestroy(&drmDir);
|
||||
}
|
||||
|
||||
const FFDisplayServerResult* ffConnectDisplayServer(const FFinstance* instance)
|
||||
{
|
||||
static FFDisplayServerResult result;
|
||||
|
||||
static pthread_mutex_t mutex = PTHREAD_MUTEX_INITIALIZER;
|
||||
static bool init = false;
|
||||
pthread_mutex_lock(&mutex);
|
||||
if(init)
|
||||
{
|
||||
pthread_mutex_unlock(&mutex);
|
||||
return &result;
|
||||
}
|
||||
init = true;
|
||||
|
||||
ffStrbufInit(&result.wmProcessName);
|
||||
ffStrbufInit(&result.wmPrettyName);
|
||||
ffStrbufInit(&result.wmProtocolName);
|
||||
ffStrbufInit(&result.deProcessName);
|
||||
ffStrbufInit(&result.dePrettyName);
|
||||
ffStrbufInit(&result.deVersion);
|
||||
ffListInitA(&result.resolutions, sizeof(FFResolutionResult), 4);
|
||||
|
||||
//We try wayland as our prefered display server, as it supports the most features.
|
||||
//This method can't detect the name of our WM / DE
|
||||
ffdsConnectWayland(instance, &result);
|
||||
|
||||
//Try the x11 libs, from most feature rich to least.
|
||||
//We use the resolution list to detect if a connection is needed.
|
||||
//They respect wmProtocolName, and only detect resolution if it is set.
|
||||
|
||||
if(result.resolutions.length == 0)
|
||||
ffdsConnectXcbRandr(instance, &result);
|
||||
|
||||
if(result.resolutions.length == 0)
|
||||
ffdsConnectXrandr(instance, &result);
|
||||
|
||||
if(result.resolutions.length == 0)
|
||||
ffdsConnectXcb(instance, &result);
|
||||
|
||||
if(result.resolutions.length == 0)
|
||||
ffdsConnectXlib(instance, &result);
|
||||
|
||||
//This resolution detection method is display server independent.
|
||||
//Use it if all connections failed
|
||||
if(result.resolutions.length == 0)
|
||||
parseDRM(&result);
|
||||
|
||||
//This fills in missing information about WM / DE by using env vars and iterating processes
|
||||
ffdsDetectWMDE(instance, &result);
|
||||
|
||||
pthread_mutex_unlock(&mutex);
|
||||
return &result;
|
||||
}
|
||||
@@ -1,26 +0,0 @@
|
||||
#pragma once
|
||||
|
||||
#ifndef FASTFETCH_INCLUDED_DISPLAYSERVER
|
||||
#define FASTFETCH_INCLUDED_DISPLAYSERVER
|
||||
|
||||
#include "fastfetch.h"
|
||||
#include "detection/displayserver.h"
|
||||
|
||||
#define FF_DISPLAYSERVER_PROTOCOL_WAYLAND "Wayland"
|
||||
#define FF_DISPLAYSERVER_PROTOCOL_X11 "X11"
|
||||
#define FF_DISPLAYSERVER_PROTOCOL_TTY "TTY"
|
||||
|
||||
uint32_t ffdsParseRefreshRate(int32_t refreshRate);
|
||||
bool ffdsAppendResolution(FFDisplayServerResult* result, uint32_t width, uint32_t height, uint32_t refreshRate);
|
||||
|
||||
void ffdsConnectWayland(const FFinstance* instance, FFDisplayServerResult* result);
|
||||
|
||||
void ffdsConnectXcbRandr(const FFinstance* instance, FFDisplayServerResult* result);
|
||||
void ffdsConnectXcb(const FFinstance* instance, FFDisplayServerResult* result);
|
||||
|
||||
void ffdsConnectXrandr(const FFinstance* instance, FFDisplayServerResult* result);
|
||||
void ffdsConnectXlib(const FFinstance* instance, FFDisplayServerResult* result);
|
||||
|
||||
void ffdsDetectWMDE(const FFinstance* instance, FFDisplayServerResult* result);
|
||||
|
||||
#endif
|
||||
@@ -1,181 +0,0 @@
|
||||
#define _GNU_SOURCE //required for struct ucred
|
||||
|
||||
#include "displayServer.h"
|
||||
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
|
||||
#ifdef FF_HAVE_WAYLAND
|
||||
#include "common/library.h"
|
||||
#include "common/io.h"
|
||||
#include <pthread.h>
|
||||
#include <wayland-client.h>
|
||||
#include <sys/socket.h>
|
||||
|
||||
typedef struct WaylandData
|
||||
{
|
||||
FFlist* results;
|
||||
FF_LIBRARY_SYMBOL(wl_proxy_marshal_constructor_versioned)
|
||||
FF_LIBRARY_SYMBOL(wl_proxy_add_listener)
|
||||
const struct wl_interface* ffwl_output_interface;
|
||||
} WaylandData;
|
||||
|
||||
static void waylandDetectWM(int fd, FFDisplayServerResult* result)
|
||||
{
|
||||
struct ucred ucred;
|
||||
socklen_t len = sizeof(struct ucred);
|
||||
if (getsockopt(fd, SOL_SOCKET, SO_PEERCRED, &ucred, &len) == -1)
|
||||
return;
|
||||
|
||||
FFstrbuf procPath;
|
||||
ffStrbufInit(&procPath);
|
||||
ffStrbufAppendF(&procPath, "/proc/%d/cmdline", ucred.pid); //We check the cmdline for the process name, because it is not trimmed.
|
||||
ffReadFileBuffer(procPath.chars, &result->wmProcessName);
|
||||
ffStrbufSubstrBeforeFirstC(&result->wmProcessName, '\0'); //Trim the arguments
|
||||
ffStrbufSubstrAfterLastC(&result->wmProcessName, '/'); //Trim the path
|
||||
ffStrbufDestroy(&procPath);
|
||||
}
|
||||
|
||||
static void stubListener(void* data, ...)
|
||||
{
|
||||
(void) data;
|
||||
}
|
||||
|
||||
static void waylandOutputModeListener(void* data, struct wl_output* output, uint32_t flags, int32_t width, int32_t height, int32_t refreshRate)
|
||||
{
|
||||
FF_UNUSED(output);
|
||||
|
||||
WaylandData* wldata = data;
|
||||
|
||||
if(!(flags & WL_OUTPUT_MODE_CURRENT) || width <= 0 || height <= 0)
|
||||
return;
|
||||
|
||||
static pthread_mutex_t mutex = PTHREAD_MUTEX_INITIALIZER;
|
||||
pthread_mutex_lock(&mutex);
|
||||
|
||||
FFResolutionResult* result = ffListAdd(wldata->results);
|
||||
|
||||
pthread_mutex_unlock(&mutex);
|
||||
|
||||
result->width = (uint32_t) width;
|
||||
result->height = (uint32_t) height;
|
||||
result->refreshRate = ffdsParseRefreshRate(refreshRate / 1000);
|
||||
}
|
||||
|
||||
static void waylandGlobalAddListener(void* data, struct wl_registry* registry, uint32_t name, const char* interface, uint32_t version)
|
||||
{
|
||||
WaylandData* wldata = data;
|
||||
|
||||
if(strcmp(interface, wldata->ffwl_output_interface->name) == 0)
|
||||
{
|
||||
struct wl_proxy* output = wldata->ffwl_proxy_marshal_constructor_versioned((struct wl_proxy*) registry, WL_REGISTRY_BIND, wldata->ffwl_output_interface, version, name, wldata->ffwl_output_interface->name, version, NULL);
|
||||
if(output == NULL)
|
||||
return;
|
||||
|
||||
//Needs to be static, because the scope of the function will already be lost when calling the listener
|
||||
static struct wl_output_listener outputListener = {
|
||||
.mode = waylandOutputModeListener,
|
||||
.geometry = (void*) stubListener,
|
||||
|
||||
//https://lists.freedesktop.org/archives/wayland-devel/2013-July/010278.html
|
||||
#if WAYLAND_VERSION_MINOR >= 2
|
||||
.done = (void*) stubListener,
|
||||
.scale = (void*) stubListener,
|
||||
#endif
|
||||
|
||||
//https://lists.freedesktop.org/archives/wayland-devel/2021-December/042064.html
|
||||
#if WAYLAND_VERSION_MINOR >= 20
|
||||
.name = (void*) stubListener,
|
||||
.description = (void*) stubListener,
|
||||
#endif
|
||||
};
|
||||
|
||||
wldata->ffwl_proxy_add_listener(output, (void(**)(void)) &outputListener, data);
|
||||
}
|
||||
}
|
||||
|
||||
bool detectWayland(const FFinstance* instance, FFDisplayServerResult* result)
|
||||
{
|
||||
FF_LIBRARY_LOAD(wayland, instance->config.libWayland, false, "libwayland-client.so", 1)
|
||||
|
||||
FF_LIBRARY_LOAD_SYMBOL(wayland, wl_display_connect, false)
|
||||
FF_LIBRARY_LOAD_SYMBOL(wayland, wl_display_get_fd, false)
|
||||
FF_LIBRARY_LOAD_SYMBOL(wayland, wl_display_dispatch, false)
|
||||
FF_LIBRARY_LOAD_SYMBOL(wayland, wl_display_roundtrip, false)
|
||||
FF_LIBRARY_LOAD_SYMBOL(wayland, wl_proxy_marshal_constructor, false)
|
||||
FF_LIBRARY_LOAD_SYMBOL(wayland, wl_display_disconnect, false)
|
||||
FF_LIBRARY_LOAD_SYMBOL(wayland, wl_registry_interface, false)
|
||||
|
||||
WaylandData data;
|
||||
|
||||
FF_LIBRARY_LOAD_SYMBOL_ADRESS(wayland, data.ffwl_proxy_marshal_constructor_versioned, wl_proxy_marshal_constructor_versioned, false)
|
||||
FF_LIBRARY_LOAD_SYMBOL_ADRESS(wayland, data.ffwl_proxy_add_listener, wl_proxy_add_listener, false)
|
||||
FF_LIBRARY_LOAD_SYMBOL_ADRESS(wayland, data.ffwl_output_interface, wl_output_interface, false)
|
||||
|
||||
struct wl_display* display = ffwl_display_connect(NULL);
|
||||
if(display == NULL)
|
||||
{
|
||||
dlclose(wayland);
|
||||
return false;
|
||||
}
|
||||
|
||||
waylandDetectWM(ffwl_display_get_fd(display), result);
|
||||
|
||||
struct wl_proxy* registry = ffwl_proxy_marshal_constructor((struct wl_proxy*) display, WL_DISPLAY_GET_REGISTRY, ffwl_registry_interface, NULL);
|
||||
if(registry == NULL)
|
||||
{
|
||||
ffwl_display_disconnect(display);
|
||||
dlclose(wayland);
|
||||
return false;
|
||||
}
|
||||
|
||||
data.results = &result->resolutions;
|
||||
|
||||
struct wl_registry_listener registry_listener = {
|
||||
.global = waylandGlobalAddListener,
|
||||
.global_remove = (void*) stubListener
|
||||
};
|
||||
|
||||
data.ffwl_proxy_add_listener(registry, (void(**)(void)) ®istry_listener, &data);
|
||||
ffwl_display_dispatch(display);
|
||||
ffwl_display_roundtrip(display);
|
||||
|
||||
ffwl_display_disconnect(display); //This will also destroy our wl_registry and wl_output proxies
|
||||
dlclose(wayland);
|
||||
|
||||
//We successfully connected to wayland and detected the resolution.
|
||||
//So we can set set the session type to wayland.
|
||||
//This is used as an indicator that we are running wayland by the x11 backends.
|
||||
ffStrbufSetS(&result->wmProtocolName, FF_DISPLAYSERVER_PROTOCOL_WAYLAND);
|
||||
return true;
|
||||
}
|
||||
#endif
|
||||
|
||||
void ffdsConnectWayland(const FFinstance* instance, FFDisplayServerResult* result)
|
||||
{
|
||||
//Wayland requires this to be set
|
||||
if(getenv("XDG_RUNTIME_DIR") == NULL)
|
||||
return;
|
||||
|
||||
#ifdef FF_HAVE_WAYLAND
|
||||
if(detectWayland(instance, result))
|
||||
return;
|
||||
#else
|
||||
FF_UNUSED(instance);
|
||||
#endif
|
||||
|
||||
const char* xdgSessionType = getenv("XDG_SESSION_TYPE");
|
||||
|
||||
//If XDG_SESSION_TYPE is set, and doesn't contain "wayland", we are probably not running in a wayland session.
|
||||
if(xdgSessionType != NULL && strcasecmp(xdgSessionType, "wayland") != 0)
|
||||
return;
|
||||
|
||||
//If XDG_SESSION_TYPE is not set, check if WAYLAND_DISPLAY or WAYLAND_SOCKET is set.
|
||||
//If not, there is no indicator for a wayland session
|
||||
if(xdgSessionType == NULL && getenv("WAYLAND_DISPLAY") == NULL && getenv("WAYLAND_SOCKET") == NULL)
|
||||
return;
|
||||
|
||||
//We are probably running a wayland compositor at this point,
|
||||
//but fastfetch was compiled without the required library, or loading the library failed.
|
||||
ffStrbufSetS(&result->wmProtocolName, FF_DISPLAYSERVER_PROTOCOL_WAYLAND);
|
||||
}
|
||||
@@ -1,461 +0,0 @@
|
||||
#include "displayServer.h"
|
||||
#include "common/io.h"
|
||||
#include "common/properties.h"
|
||||
#include "common/parsing.h"
|
||||
#include "common/processing.h"
|
||||
|
||||
#include <stdlib.h>
|
||||
#include <ctype.h>
|
||||
#include <string.h>
|
||||
#include <unistd.h>
|
||||
#include <dirent.h>
|
||||
#include <sys/stat.h>
|
||||
|
||||
static const char* parseEnv()
|
||||
{
|
||||
const char* env;
|
||||
|
||||
env = getenv("XDG_CURRENT_DESKTOP");
|
||||
if(ffStrSet(env))
|
||||
return env;
|
||||
|
||||
env = getenv("XDG_SESSION_DESKTOP");
|
||||
if(ffStrSet(env))
|
||||
return env;
|
||||
|
||||
env = getenv("CURRENT_DESKTOP");
|
||||
if(ffStrSet(env))
|
||||
return env;
|
||||
|
||||
env = getenv("SESSION_DESKTOP");
|
||||
if(ffStrSet(env))
|
||||
return env;
|
||||
|
||||
env = getenv("DESKTOP_SESSION");
|
||||
if(ffStrSet(env))
|
||||
return env;
|
||||
|
||||
if(getenv("KDE_FULL_SESSION") != NULL || getenv("KDE_SESSION_UID") != NULL || getenv("KDE_SESSION_VERSION") != NULL)
|
||||
return "KDE";
|
||||
|
||||
if(getenv("GNOME_DESKTOP_SESSION_ID") != NULL)
|
||||
return "Gnome";
|
||||
|
||||
if(getenv("MATE_DESKTOP_SESSION_ID") != NULL)
|
||||
return "Mate";
|
||||
|
||||
if(getenv("TDE_FULL_SESSION") != NULL)
|
||||
return "Trinity";
|
||||
|
||||
if(
|
||||
getenv("WAYLAND_DISPLAY") != NULL &&
|
||||
ffFileExists("/mnt/wslg/", S_IFDIR)
|
||||
) return "WSLg";
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
static void applyPrettyNameIfWM(FFDisplayServerResult* result, const char* processName)
|
||||
{
|
||||
if(!ffStrSet(processName))
|
||||
return;
|
||||
|
||||
if(
|
||||
strcasecmp(processName, "kwin_wayland") == 0 ||
|
||||
strcasecmp(processName, "kwin_wayland_wrapper") == 0 ||
|
||||
strcasecmp(processName, "kwin_x11") == 0 ||
|
||||
strcasecmp(processName, "kwin_x11_wrapper") == 0 ||
|
||||
strcasecmp(processName, "kwin") == 0
|
||||
) ffStrbufSetS(&result->wmPrettyName, "KWin");
|
||||
else if(
|
||||
strcasecmp(processName, "gnome-shell") == 0 ||
|
||||
strcasecmp(processName, "gnome shell") == 0 ||
|
||||
strcasecmp(processName, "gnome-session-binary") == 0 ||
|
||||
strcasecmp(processName, "Mutter") == 0
|
||||
) ffStrbufSetS(&result->wmPrettyName, "Mutter");
|
||||
else if(
|
||||
strcasecmp(processName, "cinnamon-session") == 0 ||
|
||||
strcasecmp(processName, "Muffin") == 0
|
||||
) ffStrbufSetS(&result->wmPrettyName, "Muffin");
|
||||
else if(strcasecmp(processName, "sway") == 0)
|
||||
ffStrbufSetS(&result->wmPrettyName, "Sway");
|
||||
else if(strcasecmp(processName, "weston") == 0)
|
||||
ffStrbufSetS(&result->wmPrettyName, "Weston");
|
||||
else if(strcasecmp(processName, "wayfire") == 0)
|
||||
ffStrbufSetS(&result->wmPrettyName, "Wayfire");
|
||||
else if(strcasecmp(processName, "openbox") == 0)
|
||||
ffStrbufSetS(&result->wmPrettyName, "Openbox");
|
||||
else if(strcasecmp(processName, "xfwm4") == 0)
|
||||
ffStrbufSetS(&result->wmPrettyName, "Xfwm4");
|
||||
else if(strcasecmp(processName, "Marco") == 0)
|
||||
ffStrbufSetS(&result->wmPrettyName, "Marco");
|
||||
else if(strcasecmp(processName, "xmonad") == 0)
|
||||
ffStrbufSetS(&result->wmPrettyName, "XMonad");
|
||||
else if( // WMs where the pretty name matches the process name
|
||||
strcasecmp(processName, "dwm") == 0 ||
|
||||
strcasecmp(processName, "bspwm") == 0 ||
|
||||
strcasecmp(processName, "tinywm") == 0
|
||||
) ffStrbufSetS(&result->wmPrettyName, processName);
|
||||
|
||||
if(result->wmPrettyName.length > 0 && result->wmProcessName.length == 0)
|
||||
ffStrbufSetS(&result->wmProcessName, processName);
|
||||
}
|
||||
|
||||
static void applyBetterWM(FFDisplayServerResult* result, const char* processName)
|
||||
{
|
||||
if(!ffStrSet(processName))
|
||||
return;
|
||||
|
||||
//If it is a known wm, this will set the pretty name
|
||||
applyPrettyNameIfWM(result, processName);
|
||||
|
||||
//If it isn't a known wm, we have to set the process name our self
|
||||
ffStrbufSetS(&result->wmProcessName, processName);
|
||||
|
||||
//If it isn't a known wm, set the pretty name to the process name
|
||||
if(result->wmPrettyName.length == 0)
|
||||
ffStrbufAppend(&result->wmPrettyName, &result->wmProcessName);
|
||||
}
|
||||
|
||||
static void getKDE(FFDisplayServerResult* result)
|
||||
{
|
||||
ffStrbufSetS(&result->deProcessName, "plasmashell");
|
||||
ffStrbufSetS(&result->dePrettyName, "KDE Plasma");
|
||||
|
||||
ffParsePropFile(FASTFETCH_TARGET_DIR_USR"/share/xsessions/plasma.desktop", "X-KDE-PluginInfo-Version =", &result->deVersion);
|
||||
if(result->deVersion.length == 0)
|
||||
ffParsePropFile(FASTFETCH_TARGET_DIR_USR"/share/xsessions/plasma5.desktop", "X-KDE-PluginInfo-Version =", &result->deVersion);
|
||||
if(result->deVersion.length == 0)
|
||||
ffParsePropFile(FASTFETCH_TARGET_DIR_USR"/share/wayland-sessions/plasmawayland.desktop", "X-KDE-PluginInfo-Version =", &result->deVersion);
|
||||
if(result->deVersion.length == 0)
|
||||
ffParsePropFile(FASTFETCH_TARGET_DIR_USR"/share/wayland-sessions/plasmawayland5.desktop", "X-KDE-PluginInfo-Version =", &result->deVersion);
|
||||
|
||||
applyBetterWM(result, getenv("KDEWM"));
|
||||
}
|
||||
|
||||
static void getGnome(FFDisplayServerResult* result)
|
||||
{
|
||||
ffStrbufSetS(&result->deProcessName, "gnome-shell");
|
||||
ffStrbufSetS(&result->dePrettyName, "GNOME");
|
||||
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(FASTFETCH_TARGET_DIR_USR"/share/applications/cinnamon.desktop", "X-GNOME-Bugzilla-Version =", &result->deVersion);
|
||||
}
|
||||
|
||||
static void getMate(const FFinstance* instance, FFDisplayServerResult* result)
|
||||
{
|
||||
ffStrbufSetS(&result->deProcessName, "mate-session");
|
||||
ffStrbufSetS(&result->dePrettyName, "MATE");
|
||||
|
||||
FFstrbuf major;
|
||||
ffStrbufInit(&major);
|
||||
|
||||
FFstrbuf minor;
|
||||
ffStrbufInit(&minor);
|
||||
|
||||
FFstrbuf micro;
|
||||
ffStrbufInit(µ);
|
||||
|
||||
ffParsePropFileValues(FASTFETCH_TARGET_DIR_USR"/share/mate-about/mate-version.xml", 3, (FFpropquery[]) {
|
||||
{"<platform>", &major},
|
||||
{"<minor>", &minor},
|
||||
{"<micro>", µ}
|
||||
});
|
||||
|
||||
ffParseSemver(&result->deVersion, &major, &minor, µ);
|
||||
|
||||
ffStrbufDestroy(&major);
|
||||
ffStrbufDestroy(&minor);
|
||||
ffStrbufDestroy(µ);
|
||||
|
||||
if(result->deVersion.length == 0 && instance->config.allowSlowOperations)
|
||||
{
|
||||
ffProcessAppendStdOut(&result->deVersion, (char* const[]){
|
||||
"mate-session",
|
||||
"--version",
|
||||
NULL
|
||||
});
|
||||
|
||||
ffStrbufSubstrAfterFirstC(&result->deVersion, ' ');
|
||||
ffStrbufTrim(&result->deVersion, ' ');
|
||||
}
|
||||
}
|
||||
|
||||
static void getXFCE4(const FFinstance* instance, FFDisplayServerResult* result)
|
||||
{
|
||||
ffStrbufSetS(&result->deProcessName, "xfce4-session");
|
||||
ffStrbufSetS(&result->dePrettyName, "Xfce4");
|
||||
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)
|
||||
{
|
||||
//This is somewhat slow
|
||||
ffProcessAppendStdOut(&result->deVersion, (char* const[]){
|
||||
"xfce4-session",
|
||||
"--version",
|
||||
NULL
|
||||
});
|
||||
|
||||
ffStrbufSubstrBeforeFirstC(&result->deVersion, '(');
|
||||
ffStrbufSubstrAfterFirstC(&result->deVersion, ' ');
|
||||
ffStrbufTrim(&result->deVersion, ' ');
|
||||
}
|
||||
}
|
||||
|
||||
static void getLXQt(const FFinstance* instance, FFDisplayServerResult* result)
|
||||
{
|
||||
ffStrbufSetS(&result->deProcessName, "lxqt-session");
|
||||
ffStrbufSetS(&result->dePrettyName, "LXQt");
|
||||
ffParsePropFile(FASTFETCH_TARGET_DIR_USR"/lib/pkgconfig/lxqt.pc", "Version:", &result->deVersion);
|
||||
|
||||
if(result->deVersion.length == 0)
|
||||
ffParsePropFile(FASTFETCH_TARGET_DIR_USR"/share/cmake/lxqt/lxqt-config.cmake", "set ( LXQT_VERSION", &result->deVersion);
|
||||
if(result->deVersion.length == 0)
|
||||
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)
|
||||
{
|
||||
//This is really, really, really slow. Thank you, LXQt developers
|
||||
ffProcessAppendStdOut(&result->deVersion, (char* const[]){
|
||||
"lxqt-session",
|
||||
"-v",
|
||||
NULL
|
||||
});
|
||||
|
||||
result->deVersion.length = 0; //don't set '\0' byte
|
||||
ffParsePropLines(result->deVersion.chars , "liblxqt", &result->deVersion);
|
||||
}
|
||||
|
||||
FFstrbuf wmProcessNameBuffer;
|
||||
ffStrbufInit(&wmProcessNameBuffer);
|
||||
|
||||
ffParsePropFileConfig(instance, "lxqt/session.conf", "window_manager =", &wmProcessNameBuffer);
|
||||
applyBetterWM(result, wmProcessNameBuffer.chars);
|
||||
|
||||
ffStrbufDestroy(&wmProcessNameBuffer);
|
||||
}
|
||||
|
||||
static void applyPrettyNameIfDE(const FFinstance* instance, FFDisplayServerResult* result, const char* name)
|
||||
{
|
||||
if(!ffStrSet(name))
|
||||
return;
|
||||
|
||||
else if(
|
||||
strcasecmp(name, "KDE") == 0 ||
|
||||
strcasecmp(name, "plasma") == 0 ||
|
||||
strcasecmp(name, "plasmashell") == 0 ||
|
||||
strcasecmp(name, "plasmawayland") == 0
|
||||
) getKDE(result);
|
||||
|
||||
else if(
|
||||
strcasecmp(name, "Gnome") == 0 ||
|
||||
strcasecmp(name, "ubuntu:GNOME") == 0 ||
|
||||
strcasecmp(name, "ubuntu") == 0 ||
|
||||
strcasecmp(name, "gnome-shell") == 0
|
||||
) getGnome(result);
|
||||
|
||||
else if(
|
||||
strcasecmp(name, "X-Cinnamon") == 0 ||
|
||||
strcasecmp(name, "Cinnamon") == 0
|
||||
) getCinnamon(result);
|
||||
|
||||
else if(
|
||||
strcasecmp(name, "XFCE") == 0 ||
|
||||
strcasecmp(name, "X-XFCE") == 0 ||
|
||||
strcasecmp(name, "XFCE4") == 0 ||
|
||||
strcasecmp(name, "X-XFCE4") == 0 ||
|
||||
strcasecmp(name, "xfce4-session") == 0
|
||||
) getXFCE4(instance, result);
|
||||
|
||||
else if(
|
||||
strcasecmp(name, "MATE") == 0 ||
|
||||
strcasecmp(name, "X-MATE") == 0 ||
|
||||
strcasecmp(name, "mate-session") == 0
|
||||
) getMate(instance, result);
|
||||
|
||||
else if(
|
||||
strcasecmp(name, "LXQt") == 0 ||
|
||||
strcasecmp(name, "X-LXQT") == 0 ||
|
||||
strcasecmp(name, "lxqt-session") == 0
|
||||
) getLXQt(instance, result);
|
||||
}
|
||||
|
||||
static void getWMProtocolNameFromEnv(FFDisplayServerResult* result)
|
||||
{
|
||||
//This is only called if all connection attempts to a display server failed
|
||||
//We don't need to check for wayland here, as the wayland code will always set the protocol name to wayland
|
||||
|
||||
const char* env = getenv("XDG_SESSION_TYPE");
|
||||
if(ffStrSet(env))
|
||||
{
|
||||
if(strcasecmp(env, "x11") == 0)
|
||||
ffStrbufSetS(&result->wmProtocolName, FF_DISPLAYSERVER_PROTOCOL_X11);
|
||||
else if(strcasecmp(env, "tty") == 0)
|
||||
ffStrbufSetS(&result->wmProtocolName, FF_DISPLAYSERVER_PROTOCOL_TTY);
|
||||
else
|
||||
ffStrbufSetS(&result->wmProtocolName, env);
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
env = getenv("DISPLAY");
|
||||
if(ffStrSet(env))
|
||||
{
|
||||
ffStrbufSetS(&result->wmProtocolName, FF_DISPLAYSERVER_PROTOCOL_X11);
|
||||
return;
|
||||
}
|
||||
|
||||
env = getenv("TERM");
|
||||
if(ffStrSet(env))
|
||||
{
|
||||
ffStrbufSetS(&result->wmProtocolName, FF_DISPLAYSERVER_PROTOCOL_TTY);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
static void getFromProcDir(const FFinstance* instance, FFDisplayServerResult* result)
|
||||
{
|
||||
DIR* proc = opendir("/proc");
|
||||
if(proc == NULL)
|
||||
return;
|
||||
|
||||
FFstrbuf procPath;
|
||||
ffStrbufInitA(&procPath, 64);
|
||||
ffStrbufAppendS(&procPath, "/proc/");
|
||||
|
||||
uint32_t procPathLength = procPath.length;
|
||||
|
||||
FFstrbuf userID;
|
||||
ffStrbufInit(&userID);
|
||||
ffStrbufAppendF(&userID, "%i", getuid());
|
||||
|
||||
FFstrbuf loginuid;
|
||||
ffStrbufInit(&loginuid);
|
||||
|
||||
FFstrbuf processName;
|
||||
ffStrbufInitA(&processName, 256); //Some processes have large command lines (looking at you chrome)
|
||||
|
||||
struct dirent* dirent;
|
||||
while((dirent = readdir(proc)) != NULL)
|
||||
{
|
||||
//Match only folders starting with a number (the pid folders)
|
||||
if(dirent->d_type != DT_DIR || !isdigit(dirent->d_name[0]))
|
||||
continue;
|
||||
|
||||
ffStrbufAppendS(&procPath, dirent->d_name);
|
||||
uint32_t procFolderPathLength = procPath.length;
|
||||
|
||||
//Don't check for processes not owend by the current user.
|
||||
ffStrbufAppendS(&procPath, "/loginuid");
|
||||
ffReadFileBuffer(procPath.chars, &loginuid);
|
||||
if(ffStrbufComp(&userID, &loginuid) != 0)
|
||||
{
|
||||
ffStrbufSubstrBefore(&procPath, procPathLength);
|
||||
continue;
|
||||
}
|
||||
|
||||
ffStrbufSubstrBefore(&procPath, procFolderPathLength);
|
||||
|
||||
//We check the cmdline for the process name, because it is not trimmed.
|
||||
ffStrbufAppendS(&procPath, "/cmdline");
|
||||
ffReadFileBuffer(procPath.chars, &processName);
|
||||
ffStrbufSubstrBeforeFirstC(&processName, '\0'); //Trim the arguments
|
||||
ffStrbufSubstrAfterLastC(&processName, '/');
|
||||
|
||||
ffStrbufSubstrBefore(&procPath, procPathLength);
|
||||
|
||||
if(result->dePrettyName.length == 0)
|
||||
applyPrettyNameIfDE(instance, result, processName.chars);
|
||||
|
||||
if(result->wmPrettyName.length == 0)
|
||||
applyPrettyNameIfWM(result, processName.chars);
|
||||
|
||||
if(result->dePrettyName.length > 0 && result->wmPrettyName.length > 0)
|
||||
break;
|
||||
}
|
||||
|
||||
closedir(proc);
|
||||
|
||||
ffStrbufDestroy(&processName);
|
||||
ffStrbufDestroy(&loginuid);
|
||||
ffStrbufDestroy(&userID);
|
||||
ffStrbufDestroy(&procPath);
|
||||
}
|
||||
|
||||
void ffdsDetectWMDE(const FFinstance* instance, FFDisplayServerResult* result)
|
||||
{
|
||||
//If all connections failed, use the environment variables to detect protocol name
|
||||
if(result->wmProtocolName.length == 0)
|
||||
getWMProtocolNameFromEnv(result);
|
||||
|
||||
//We don't want to detect anything in TTY
|
||||
//This can't happen if a connection succeeded, so we don't need to clear wmProcessName
|
||||
if(ffStrbufIgnCaseCompS(&result->wmProtocolName, FF_DISPLAYSERVER_PROTOCOL_TTY) == 0)
|
||||
return;
|
||||
|
||||
const char* env = parseEnv();
|
||||
|
||||
if(result->wmProcessName.length > 0)
|
||||
{
|
||||
//If we found the processName via display server, use it.
|
||||
//This will set the pretty name if it is a known WM, otherwise the prettyName to the processName
|
||||
applyPrettyNameIfWM(result, result->wmProcessName.chars);
|
||||
if(result->wmPrettyName.length == 0)
|
||||
ffStrbufSet(&result->wmPrettyName, &result->wmProcessName);
|
||||
}
|
||||
else
|
||||
{
|
||||
//if env is a known WM, use it
|
||||
applyPrettyNameIfWM(result, env);
|
||||
}
|
||||
|
||||
//Connecting to a display server only gives WM results, not DE results.
|
||||
//If we find it in the environment, use that.
|
||||
applyPrettyNameIfDE(instance, result, env);
|
||||
|
||||
//If WM was found by connection to the sever, and DE in the environment, we can return
|
||||
//This way we never call getFromProcDir(), which has slow initalization time
|
||||
if(result->dePrettyName.length > 0 && result->wmPrettyName.length > 0)
|
||||
return;
|
||||
|
||||
//Get missing WM / DE from processes.
|
||||
getFromProcDir(instance, result);
|
||||
|
||||
//Return if both wm and de are set, or if env doesn't contain anything
|
||||
if(
|
||||
(result->wmPrettyName.length > 0 && result->dePrettyName.length > 0) ||
|
||||
!ffStrSet(env)
|
||||
) return;
|
||||
|
||||
//If nothing is set, use env as WM
|
||||
else if(result->wmPrettyName.length == 0 && result->dePrettyName.length == 0)
|
||||
{
|
||||
ffStrbufSetS(&result->wmProcessName, env);
|
||||
ffStrbufSetS(&result->wmPrettyName, env);
|
||||
}
|
||||
|
||||
//If only WM is not set, and DE doesn't equal env, use env as WM
|
||||
else if(
|
||||
result->wmPrettyName.length == 0 &&
|
||||
ffStrbufIgnCaseCompS(&result->deProcessName, env) != 0 &&
|
||||
ffStrbufIgnCaseCompS(&result->dePrettyName, env) != 0
|
||||
) {
|
||||
ffStrbufSetS(&result->wmProcessName, env);
|
||||
ffStrbufSetS(&result->wmPrettyName, env);
|
||||
}
|
||||
|
||||
//If only DE is not set, and WM doesn't equal env, use env as DE
|
||||
else if(
|
||||
result->dePrettyName.length == 0 &&
|
||||
ffStrbufIgnCaseCompS(&result->wmProcessName, env) != 0 &&
|
||||
ffStrbufIgnCaseCompS(&result->wmPrettyName, env) != 0
|
||||
) {
|
||||
ffStrbufSetS(&result->deProcessName, env);
|
||||
ffStrbufSetS(&result->dePrettyName, env);
|
||||
}
|
||||
}
|
||||
@@ -1,395 +0,0 @@
|
||||
#include "displayServer.h"
|
||||
|
||||
#ifdef FF_HAVE_XCB
|
||||
#include "common/library.h"
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <xcb/xcb.h>
|
||||
|
||||
typedef struct XcbPropertyData
|
||||
{
|
||||
FF_LIBRARY_SYMBOL(xcb_intern_atom)
|
||||
FF_LIBRARY_SYMBOL(xcb_intern_atom_reply)
|
||||
FF_LIBRARY_SYMBOL(xcb_get_property)
|
||||
FF_LIBRARY_SYMBOL(xcb_get_property_reply)
|
||||
FF_LIBRARY_SYMBOL(xcb_get_property_value)
|
||||
FF_LIBRARY_SYMBOL(xcb_get_property_value_length)
|
||||
} XcbPropertyData;
|
||||
|
||||
static bool xcbInitPropertyData(void* libraryHandle, XcbPropertyData* propertyData)
|
||||
{
|
||||
FF_LIBRARY_LOAD_SYMBOL_ADRESS(libraryHandle, propertyData->ffxcb_intern_atom, xcb_intern_atom, false)
|
||||
FF_LIBRARY_LOAD_SYMBOL_ADRESS(libraryHandle, propertyData->ffxcb_intern_atom_reply, xcb_intern_atom_reply, false)
|
||||
FF_LIBRARY_LOAD_SYMBOL_ADRESS(libraryHandle, propertyData->ffxcb_get_property, xcb_get_property, false)
|
||||
FF_LIBRARY_LOAD_SYMBOL_ADRESS(libraryHandle, propertyData->ffxcb_get_property_reply, xcb_get_property_reply, false)
|
||||
FF_LIBRARY_LOAD_SYMBOL_ADRESS(libraryHandle, propertyData->ffxcb_get_property_value, xcb_get_property_value, false)
|
||||
FF_LIBRARY_LOAD_SYMBOL_ADRESS(libraryHandle, propertyData->ffxcb_get_property_value_length, xcb_get_property_value_length, false)
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
static void* xcbGetProperty(XcbPropertyData* data, xcb_connection_t* connection, xcb_window_t window, const char* request)
|
||||
{
|
||||
xcb_intern_atom_cookie_t requestAtomCookie = data->ffxcb_intern_atom(connection, true, (uint16_t) strlen(request), request);
|
||||
xcb_intern_atom_reply_t* requestAtomReply = data->ffxcb_intern_atom_reply(connection, requestAtomCookie, NULL);
|
||||
if(requestAtomReply == NULL)
|
||||
return NULL;
|
||||
|
||||
xcb_get_property_cookie_t propertyCookie = data->ffxcb_get_property(connection, false, window, requestAtomReply->atom, XCB_ATOM_ANY, 0, 64);
|
||||
|
||||
free(requestAtomReply);
|
||||
|
||||
xcb_get_property_reply_t* propertyReply = data->ffxcb_get_property_reply(connection, propertyCookie, NULL);
|
||||
if(propertyReply == NULL)
|
||||
return NULL;
|
||||
|
||||
int length = data->ffxcb_get_property_value_length(propertyReply);
|
||||
if(length <= 0)
|
||||
{
|
||||
free(propertyReply);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
//Why are xcb property strings not null terminated???
|
||||
void* replyValue = malloc((size_t) (length + 1));
|
||||
memcpy(replyValue, data->ffxcb_get_property_value(propertyReply), (size_t) length);
|
||||
((char*) replyValue)[length] = '\0';
|
||||
|
||||
free(propertyReply);
|
||||
|
||||
return replyValue;
|
||||
}
|
||||
|
||||
static void xcbDetectWMfromEWMH(XcbPropertyData* data, xcb_connection_t* connection, xcb_window_t rootWindow, FFDisplayServerResult* result)
|
||||
{
|
||||
if(result->wmProcessName.length > 0 || ffStrbufCompS(&result->wmProtocolName, FF_DISPLAYSERVER_PROTOCOL_WAYLAND) == 0)
|
||||
return;
|
||||
|
||||
xcb_window_t* wmWindow = (xcb_window_t*) xcbGetProperty(data, connection, rootWindow, "_NET_SUPPORTING_WM_CHECK");
|
||||
if(wmWindow == NULL)
|
||||
return;
|
||||
|
||||
char* wmName = (char*) xcbGetProperty(data, connection, *wmWindow, "_NET_WM_NAME");
|
||||
if(wmName == NULL)
|
||||
wmName = (char*) xcbGetProperty(data, connection, *wmWindow, "WM_NAME");
|
||||
|
||||
free(wmWindow);
|
||||
|
||||
if(wmName == NULL)
|
||||
return;
|
||||
|
||||
if(*wmName == '\0')
|
||||
{
|
||||
free(wmName);
|
||||
return;
|
||||
}
|
||||
|
||||
ffStrbufSetS(&result->wmProcessName, wmName);
|
||||
|
||||
free(wmName);
|
||||
}
|
||||
|
||||
void ffdsConnectXcb(const FFinstance* instance, FFDisplayServerResult* result)
|
||||
{
|
||||
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,)
|
||||
FF_LIBRARY_LOAD_SYMBOL(xcb, xcb_screen_next,)
|
||||
FF_LIBRARY_LOAD_SYMBOL(xcb, xcb_disconnect,)
|
||||
|
||||
XcbPropertyData propertyData;
|
||||
bool propertyDataInitialized = xcbInitPropertyData(xcb, &propertyData);
|
||||
|
||||
xcb_connection_t* connection = ffxcb_connect(NULL, NULL);
|
||||
if(connection == NULL)
|
||||
{
|
||||
dlclose(xcb);
|
||||
return;
|
||||
}
|
||||
|
||||
xcb_screen_iterator_t iterator = ffxcb_setup_roots_iterator(ffxcb_get_setup(connection));
|
||||
|
||||
if(iterator.rem > 0 && propertyDataInitialized)
|
||||
xcbDetectWMfromEWMH(&propertyData, connection, iterator.data->root, result);
|
||||
|
||||
while(iterator.rem > 0)
|
||||
{
|
||||
ffdsAppendResolution(
|
||||
result,
|
||||
(uint32_t) iterator.data->width_in_pixels,
|
||||
(uint32_t) iterator.data->height_in_pixels,
|
||||
0
|
||||
);
|
||||
ffxcb_screen_next(&iterator);
|
||||
}
|
||||
|
||||
ffxcb_disconnect(connection);
|
||||
dlclose(xcb);
|
||||
|
||||
//If wayland hasn't set this, connection failed for it. So we are running only a X Server, not XWayland.
|
||||
if(result->wmProtocolName.length == 0)
|
||||
ffStrbufSetS(&result->wmProtocolName, FF_DISPLAYSERVER_PROTOCOL_X11);
|
||||
}
|
||||
|
||||
#else
|
||||
|
||||
void ffdsConnectXcb(const FFinstance* instance, FFDisplayServerResult* result)
|
||||
{
|
||||
//Do nothing. There are other implementations coming
|
||||
FF_UNUSED(instance, result)
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
#ifdef FF_HAVE_XCB_RANDR
|
||||
#include <xcb/randr.h>
|
||||
|
||||
typedef struct XcbRandrData
|
||||
{
|
||||
FF_LIBRARY_SYMBOL(xcb_randr_get_screen_resources)
|
||||
FF_LIBRARY_SYMBOL(xcb_randr_get_screen_resources_reply)
|
||||
FF_LIBRARY_SYMBOL(xcb_randr_get_screen_resources_modes_iterator)
|
||||
FF_LIBRARY_SYMBOL(xcb_randr_get_screen_info)
|
||||
FF_LIBRARY_SYMBOL(xcb_randr_get_screen_info_reply)
|
||||
FF_LIBRARY_SYMBOL(xcb_randr_mode_info_next)
|
||||
FF_LIBRARY_SYMBOL(xcb_randr_get_monitors)
|
||||
FF_LIBRARY_SYMBOL(xcb_randr_get_monitors_reply)
|
||||
FF_LIBRARY_SYMBOL(xcb_randr_get_monitors_monitors_iterator)
|
||||
FF_LIBRARY_SYMBOL(xcb_randr_monitor_info_next)
|
||||
FF_LIBRARY_SYMBOL(xcb_randr_monitor_info_outputs_length)
|
||||
FF_LIBRARY_SYMBOL(xcb_randr_monitor_info_outputs)
|
||||
FF_LIBRARY_SYMBOL(xcb_randr_output_next)
|
||||
FF_LIBRARY_SYMBOL(xcb_randr_get_output_info)
|
||||
FF_LIBRARY_SYMBOL(xcb_randr_get_output_info_reply)
|
||||
FF_LIBRARY_SYMBOL(xcb_randr_get_crtc_info)
|
||||
FF_LIBRARY_SYMBOL(xcb_randr_get_crtc_info_reply)
|
||||
|
||||
//init once
|
||||
xcb_connection_t* connection;
|
||||
FFDisplayServerResult* result;
|
||||
|
||||
//init per screen
|
||||
uint32_t defaultRefreshRate;
|
||||
xcb_randr_get_screen_resources_reply_t* screenResources;
|
||||
} XcbRandrData;
|
||||
|
||||
static bool xcbRandrHandleModeInfo(XcbRandrData* data, xcb_randr_mode_info_t* modeInfo)
|
||||
{
|
||||
uint32_t refreshRate = ffdsParseRefreshRate((int32_t) (
|
||||
modeInfo->dot_clock / (uint32_t) (modeInfo->htotal * modeInfo->vtotal)
|
||||
));
|
||||
|
||||
return ffdsAppendResolution(
|
||||
data->result,
|
||||
(uint32_t) modeInfo->width,
|
||||
(uint32_t) modeInfo->height,
|
||||
refreshRate == 0 ? data->defaultRefreshRate : refreshRate
|
||||
);
|
||||
}
|
||||
|
||||
static bool xcbRandrHandleMode(XcbRandrData* data, xcb_randr_mode_t mode)
|
||||
{
|
||||
//We do the check here, because we want the best fallback resolution if this call failed
|
||||
if(data->screenResources == NULL)
|
||||
return false;
|
||||
|
||||
xcb_randr_mode_info_iterator_t modesIterator = data->ffxcb_randr_get_screen_resources_modes_iterator(data->screenResources);
|
||||
|
||||
while(modesIterator.rem > 0)
|
||||
{
|
||||
if(modesIterator.data->id == mode)
|
||||
return xcbRandrHandleModeInfo(data, modesIterator.data);
|
||||
|
||||
data->ffxcb_randr_mode_info_next(&modesIterator);
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
static bool xcbRandrHandleCrtc(XcbRandrData* data, xcb_randr_crtc_t crtc)
|
||||
{
|
||||
xcb_randr_get_crtc_info_cookie_t crtcInfoCookie = data->ffxcb_randr_get_crtc_info(data->connection, crtc, XCB_CURRENT_TIME);
|
||||
xcb_randr_get_crtc_info_reply_t* crtcInfoReply = data->ffxcb_randr_get_crtc_info_reply(data->connection, crtcInfoCookie, NULL);
|
||||
if(crtcInfoReply == NULL)
|
||||
return false;
|
||||
|
||||
bool res = xcbRandrHandleMode(data, crtcInfoReply->mode);
|
||||
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)
|
||||
{
|
||||
xcb_randr_get_output_info_cookie_t outputInfoCookie = data->ffxcb_randr_get_output_info(data->connection, output, XCB_CURRENT_TIME);
|
||||
xcb_randr_get_output_info_reply_t* outputInfoReply = data->ffxcb_randr_get_output_info_reply(data->connection, outputInfoCookie, NULL);
|
||||
if(outputInfoReply == NULL)
|
||||
return false;
|
||||
|
||||
bool res = xcbRandrHandleCrtc(data, outputInfoReply->crtc);
|
||||
|
||||
free(outputInfoReply);
|
||||
|
||||
return res;
|
||||
}
|
||||
|
||||
static bool xcbRandrHandleMonitor(XcbRandrData* data, xcb_randr_monitor_info_t* monitor)
|
||||
{
|
||||
//for some reasons, we have to construct this our self
|
||||
xcb_randr_output_iterator_t outputIterator = {
|
||||
.index = 0,
|
||||
.data = data->ffxcb_randr_monitor_info_outputs(monitor),
|
||||
.rem = data->ffxcb_randr_monitor_info_outputs_length(monitor)
|
||||
};
|
||||
|
||||
bool foundOutput = false;
|
||||
|
||||
while(outputIterator.rem > 0)
|
||||
{
|
||||
if(xcbRandrHandleOutput(data, *outputIterator.data))
|
||||
foundOutput = true;
|
||||
data->ffxcb_randr_output_next(&outputIterator);
|
||||
};
|
||||
|
||||
return foundOutput ? true : ffdsAppendResolution(
|
||||
data->result,
|
||||
(uint32_t) monitor->width,
|
||||
(uint32_t) monitor->height,
|
||||
data->defaultRefreshRate
|
||||
);
|
||||
}
|
||||
|
||||
static bool xcbRandrHandleMonitors(XcbRandrData* data, xcb_screen_t* screen)
|
||||
{
|
||||
xcb_randr_get_monitors_cookie_t monitorsCookie = data->ffxcb_randr_get_monitors(data->connection, screen->root, true);
|
||||
xcb_randr_get_monitors_reply_t* monitorsReply = data->ffxcb_randr_get_monitors_reply(data->connection, monitorsCookie, NULL);
|
||||
if(monitorsReply == NULL)
|
||||
return false;
|
||||
|
||||
xcb_randr_monitor_info_iterator_t monitorInfoIterator = data->ffxcb_randr_get_monitors_monitors_iterator(monitorsReply);
|
||||
|
||||
bool foundMonitor = false;
|
||||
|
||||
while(monitorInfoIterator.rem > 0)
|
||||
{
|
||||
if(xcbRandrHandleMonitor(data, monitorInfoIterator.data))
|
||||
foundMonitor = true;
|
||||
data->ffxcb_randr_monitor_info_next(&monitorInfoIterator);
|
||||
}
|
||||
|
||||
free(monitorsReply);
|
||||
|
||||
return foundMonitor;
|
||||
}
|
||||
|
||||
static void xcbRandrHandleScreen(XcbRandrData* data, xcb_screen_t* screen)
|
||||
{
|
||||
//Init screen info. This is used to get the default refresh rate. If this fails, default refresh rate is simply 0.
|
||||
xcb_randr_get_screen_info_cookie_t screenInfoCookie = data->ffxcb_randr_get_screen_info(data->connection, screen->root);
|
||||
xcb_randr_get_screen_info_reply_t* screenInfoReply = data->ffxcb_randr_get_screen_info_reply(data->connection, screenInfoCookie, NULL);
|
||||
|
||||
if(screenInfoReply != NULL)
|
||||
{
|
||||
data->defaultRefreshRate = screenInfoReply->rate;
|
||||
free(screenInfoReply);
|
||||
}
|
||||
else
|
||||
data->defaultRefreshRate = 0;
|
||||
|
||||
//Init screen resources. They are used to iterate over all modes. xcbRandrHandleMode checks for " == NULL", to fail as late as possible.
|
||||
xcb_randr_get_screen_resources_cookie_t screenResourcesCookie = data->ffxcb_randr_get_screen_resources(data->connection, screen->root);
|
||||
data->screenResources = data->ffxcb_randr_get_screen_resources_reply(data->connection, screenResourcesCookie, NULL);
|
||||
|
||||
//With all the initialisation done, start the detection
|
||||
bool ret = xcbRandrHandleMonitors(data, screen);
|
||||
|
||||
free(data->screenResources);
|
||||
|
||||
if(ret)
|
||||
return;
|
||||
|
||||
//If detetction failed, fallback to screen = monitor, like in the libxcb.so implementation
|
||||
ffdsAppendResolution(
|
||||
data->result,
|
||||
(uint32_t) screen->width_in_pixels,
|
||||
(uint32_t) screen->height_in_pixels,
|
||||
data->defaultRefreshRate
|
||||
);
|
||||
}
|
||||
|
||||
void ffdsConnectXcbRandr(const FFinstance* instance, FFDisplayServerResult* result)
|
||||
{
|
||||
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,)
|
||||
FF_LIBRARY_LOAD_SYMBOL(xcbRandr, xcb_screen_next,)
|
||||
FF_LIBRARY_LOAD_SYMBOL(xcbRandr, xcb_disconnect,)
|
||||
|
||||
XcbRandrData data;
|
||||
|
||||
FF_LIBRARY_LOAD_SYMBOL_ADRESS(xcbRandr, data.ffxcb_randr_get_screen_resources, xcb_randr_get_screen_resources,)
|
||||
FF_LIBRARY_LOAD_SYMBOL_ADRESS(xcbRandr, data.ffxcb_randr_get_screen_resources_reply, xcb_randr_get_screen_resources_reply,)
|
||||
FF_LIBRARY_LOAD_SYMBOL_ADRESS(xcbRandr, data.ffxcb_randr_get_screen_resources_modes_iterator, xcb_randr_get_screen_resources_modes_iterator,)
|
||||
FF_LIBRARY_LOAD_SYMBOL_ADRESS(xcbRandr, data.ffxcb_randr_get_screen_info, xcb_randr_get_screen_info,)
|
||||
FF_LIBRARY_LOAD_SYMBOL_ADRESS(xcbRandr, data.ffxcb_randr_get_screen_info_reply, xcb_randr_get_screen_info_reply,)
|
||||
FF_LIBRARY_LOAD_SYMBOL_ADRESS(xcbRandr, data.ffxcb_randr_mode_info_next, xcb_randr_mode_info_next,)
|
||||
FF_LIBRARY_LOAD_SYMBOL_ADRESS(xcbRandr, data.ffxcb_randr_get_monitors, xcb_randr_get_monitors,)
|
||||
FF_LIBRARY_LOAD_SYMBOL_ADRESS(xcbRandr, data.ffxcb_randr_get_monitors_reply, xcb_randr_get_monitors_reply,)
|
||||
FF_LIBRARY_LOAD_SYMBOL_ADRESS(xcbRandr, data.ffxcb_randr_get_monitors_monitors_iterator, xcb_randr_get_monitors_monitors_iterator,)
|
||||
FF_LIBRARY_LOAD_SYMBOL_ADRESS(xcbRandr, data.ffxcb_randr_monitor_info_next, xcb_randr_monitor_info_next,)
|
||||
FF_LIBRARY_LOAD_SYMBOL_ADRESS(xcbRandr, data.ffxcb_randr_monitor_info_outputs_length, xcb_randr_monitor_info_outputs_length,)
|
||||
FF_LIBRARY_LOAD_SYMBOL_ADRESS(xcbRandr, data.ffxcb_randr_monitor_info_outputs, xcb_randr_monitor_info_outputs,)
|
||||
FF_LIBRARY_LOAD_SYMBOL_ADRESS(xcbRandr, data.ffxcb_randr_output_next, xcb_randr_output_next,)
|
||||
FF_LIBRARY_LOAD_SYMBOL_ADRESS(xcbRandr, data.ffxcb_randr_get_output_info, xcb_randr_get_output_info,)
|
||||
FF_LIBRARY_LOAD_SYMBOL_ADRESS(xcbRandr, data.ffxcb_randr_get_output_info_reply, xcb_randr_get_output_info_reply,)
|
||||
FF_LIBRARY_LOAD_SYMBOL_ADRESS(xcbRandr, data.ffxcb_randr_get_crtc_info, xcb_randr_get_crtc_info,)
|
||||
FF_LIBRARY_LOAD_SYMBOL_ADRESS(xcbRandr, data.ffxcb_randr_get_crtc_info_reply, xcb_randr_get_crtc_info_reply,)
|
||||
|
||||
XcbPropertyData propertyData;
|
||||
bool propertyDataInitialized = xcbInitPropertyData(xcbRandr, &propertyData);
|
||||
|
||||
data.connection = ffxcb_connect(NULL, NULL);
|
||||
if(data.connection == NULL)
|
||||
{
|
||||
dlclose(xcbRandr);
|
||||
return;
|
||||
}
|
||||
|
||||
data.result = result;
|
||||
|
||||
xcb_screen_iterator_t iterator = ffxcb_setup_roots_iterator(ffxcb_get_setup(data.connection));
|
||||
|
||||
if(iterator.rem > 0 && propertyDataInitialized)
|
||||
xcbDetectWMfromEWMH(&propertyData, data.connection, iterator.data->root, result);
|
||||
|
||||
while(iterator.rem > 0)
|
||||
{
|
||||
xcbRandrHandleScreen(&data, iterator.data);
|
||||
ffxcb_screen_next(&iterator);
|
||||
}
|
||||
|
||||
ffxcb_disconnect(data.connection);
|
||||
dlclose(xcbRandr);
|
||||
|
||||
//If wayland hasn't set this, connection failed for it. So we are running only a X Server, not XWayland.
|
||||
if(result->wmProtocolName.length == 0)
|
||||
ffStrbufSetS(&result->wmProtocolName, FF_DISPLAYSERVER_PROTOCOL_X11);
|
||||
}
|
||||
|
||||
#else
|
||||
|
||||
void ffdsConnectXcbRandr(const FFinstance* instance, FFDisplayServerResult* result)
|
||||
{
|
||||
//Do nothing. There are other implementations coming
|
||||
FF_UNUSED(instance, result)
|
||||
}
|
||||
|
||||
#endif
|
||||
@@ -1,315 +0,0 @@
|
||||
#include "displayServer.h"
|
||||
|
||||
#ifdef FF_HAVE_X11
|
||||
#include "common/library.h"
|
||||
#include "common/parsing.h"
|
||||
#include <X11/Xlib.h>
|
||||
|
||||
typedef struct X11PropertyData
|
||||
{
|
||||
FF_LIBRARY_SYMBOL(XInternAtom)
|
||||
FF_LIBRARY_SYMBOL(XGetWindowProperty)
|
||||
} X11PropertyData;
|
||||
|
||||
static bool x11InitPropertyData(void* libraryHandle, X11PropertyData* propertyData)
|
||||
{
|
||||
FF_LIBRARY_LOAD_SYMBOL_ADRESS(libraryHandle, propertyData->ffXInternAtom, XInternAtom, false)
|
||||
FF_LIBRARY_LOAD_SYMBOL_ADRESS(libraryHandle, propertyData->ffXGetWindowProperty, XGetWindowProperty, false)
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
static unsigned char* x11GetProperty(X11PropertyData* data, Display* display, Window window, const char* request)
|
||||
{
|
||||
Atom requestAtom = data->ffXInternAtom(display, request, False);
|
||||
if(requestAtom == None)
|
||||
return NULL;
|
||||
|
||||
Atom actualType;
|
||||
unsigned long unused;
|
||||
unsigned char* result = NULL;
|
||||
|
||||
data->ffXGetWindowProperty(display, window, requestAtom, 0, 64, False, AnyPropertyType, &actualType, (int*) &unused, &unused, &unused, &result);
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
static void x11DetectWMFromEWMH(X11PropertyData* data, Display* display, FFDisplayServerResult* result)
|
||||
{
|
||||
if(result->wmProcessName.length > 0 || ffStrbufCompS(&result->wmProtocolName, FF_DISPLAYSERVER_PROTOCOL_WAYLAND) == 0)
|
||||
return;
|
||||
|
||||
Window* wmWindow = (Window*) x11GetProperty(data, display, DefaultRootWindow(display), "_NET_SUPPORTING_WM_CHECK");
|
||||
if(wmWindow == NULL)
|
||||
return;
|
||||
|
||||
const char* wmName = (const char*) x11GetProperty(data, display, *wmWindow, "_NET_WM_NAME");
|
||||
if(wmName == NULL)
|
||||
wmName = (const char*) x11GetProperty(data, display, *wmWindow, "WM_NAME");
|
||||
|
||||
if(!ffStrSet(wmName))
|
||||
return;
|
||||
|
||||
ffStrbufSetS(&result->wmProcessName, wmName);
|
||||
}
|
||||
|
||||
void ffdsConnectXlib(const FFinstance* instance, FFDisplayServerResult* result)
|
||||
{
|
||||
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,)
|
||||
|
||||
X11PropertyData propertyData;
|
||||
bool propertyDataInitialized = x11InitPropertyData(x11, &propertyData);
|
||||
|
||||
Display* display = ffXOpenDisplay(x11);
|
||||
if(display == NULL)
|
||||
{
|
||||
dlclose(x11);
|
||||
return;
|
||||
}
|
||||
|
||||
if(propertyDataInitialized && ScreenCount(display) > 0)
|
||||
x11DetectWMFromEWMH(&propertyData, display, result);
|
||||
|
||||
for(int i = 0; i < ScreenCount(display); i++)
|
||||
{
|
||||
Screen* screen = ScreenOfDisplay(display, i);
|
||||
ffdsAppendResolution(
|
||||
result,
|
||||
(uint32_t) screen->width,
|
||||
(uint32_t) screen->height,
|
||||
0
|
||||
);
|
||||
}
|
||||
|
||||
ffXCloseDisplay(display);
|
||||
dlclose(x11);
|
||||
|
||||
//If wayland hasn't set this, connection failed for it. So we are running only a X Server, not XWayland.
|
||||
if(result->wmProtocolName.length == 0)
|
||||
ffStrbufSetS(&result->wmProtocolName, FF_DISPLAYSERVER_PROTOCOL_X11);
|
||||
}
|
||||
|
||||
#else
|
||||
|
||||
void ffdsConnectXlib(const FFinstance* instance, FFDisplayServerResult* result)
|
||||
{
|
||||
//Do nothing. WM / DE detection will use environment vars to detect as much as possible.
|
||||
FF_UNUSED(instance, result);
|
||||
}
|
||||
|
||||
#endif //FF_HAVE_X11
|
||||
|
||||
#ifdef FF_HAVE_XRANDR
|
||||
#include <X11/extensions/Xrandr.h>
|
||||
|
||||
typedef struct XrandrData
|
||||
{
|
||||
FF_LIBRARY_SYMBOL(XRRGetScreenInfo)
|
||||
FF_LIBRARY_SYMBOL(XRRConfigCurrentConfiguration)
|
||||
FF_LIBRARY_SYMBOL(XRRConfigCurrentRate)
|
||||
FF_LIBRARY_SYMBOL(XRRGetMonitors)
|
||||
FF_LIBRARY_SYMBOL(XRRGetScreenResources)
|
||||
FF_LIBRARY_SYMBOL(XRRGetOutputInfo)
|
||||
FF_LIBRARY_SYMBOL(XRRGetCrtcInfo)
|
||||
FF_LIBRARY_SYMBOL(XRRFreeCrtcInfo)
|
||||
FF_LIBRARY_SYMBOL(XRRFreeOutputInfo)
|
||||
FF_LIBRARY_SYMBOL(XRRFreeScreenResources)
|
||||
FF_LIBRARY_SYMBOL(XRRFreeMonitors)
|
||||
FF_LIBRARY_SYMBOL(XRRFreeScreenConfigInfo)
|
||||
|
||||
//Init once
|
||||
Display* display;
|
||||
FFDisplayServerResult* result;
|
||||
|
||||
//Init per screen
|
||||
uint32_t defaultRefreshRate;
|
||||
XRRScreenResources* screenResources;
|
||||
} XrandrData;
|
||||
|
||||
static bool xrandrHandleModeInfo(XrandrData* data, XRRModeInfo* modeInfo)
|
||||
{
|
||||
uint32_t refreshRate = ffdsParseRefreshRate((int32_t) (
|
||||
modeInfo->dotClock / (modeInfo->hTotal * modeInfo->vTotal)
|
||||
));
|
||||
|
||||
return ffdsAppendResolution(
|
||||
data->result,
|
||||
(uint32_t) modeInfo->width,
|
||||
(uint32_t) modeInfo->height,
|
||||
refreshRate == 0 ? data->defaultRefreshRate : refreshRate
|
||||
);
|
||||
}
|
||||
|
||||
static bool xrandrHandleMode(XrandrData* data, RRMode mode)
|
||||
{
|
||||
for(int i = 0; i < data->screenResources->nmode; i++)
|
||||
{
|
||||
if(data->screenResources->modes[i].id == mode)
|
||||
return xrandrHandleModeInfo(data, &data->screenResources->modes[i]);
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
static bool xrandrHandleCrtc(XrandrData* data, RRCrtc crtc)
|
||||
{
|
||||
//We do the check here, because we want the best fallback resolution if this call failed
|
||||
if(data->screenResources == NULL)
|
||||
return false;
|
||||
|
||||
XRRCrtcInfo* crtcInfo = data->ffXRRGetCrtcInfo(data->display, data->screenResources, crtc);
|
||||
if(crtcInfo == NULL)
|
||||
return false;
|
||||
|
||||
bool res = xrandrHandleMode(data, crtcInfo->mode);
|
||||
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)
|
||||
{
|
||||
XRROutputInfo* outputInfo = data->ffXRRGetOutputInfo(data->display, data->screenResources, output);
|
||||
if(outputInfo == NULL)
|
||||
return false;
|
||||
|
||||
bool res = xrandrHandleCrtc(data, outputInfo->crtc);
|
||||
|
||||
data->ffXRRFreeOutputInfo(outputInfo);
|
||||
|
||||
return res;
|
||||
}
|
||||
|
||||
static bool xrandrHandleMonitor(XrandrData* data, XRRMonitorInfo* monitorInfo)
|
||||
{
|
||||
bool foundOutput = false;
|
||||
|
||||
for(int i = 0; i < monitorInfo->noutput; i++)
|
||||
{
|
||||
if(xrandrHandleOutput(data, monitorInfo->outputs[i]))
|
||||
foundOutput = true;
|
||||
}
|
||||
|
||||
return foundOutput ? true : ffdsAppendResolution(
|
||||
data->result,
|
||||
(uint32_t) monitorInfo->width,
|
||||
(uint32_t) monitorInfo->height,
|
||||
data->defaultRefreshRate
|
||||
);
|
||||
}
|
||||
|
||||
static bool xrandrHandleMonitors(XrandrData* data, Screen* screen)
|
||||
{
|
||||
int numberOfMonitors;
|
||||
XRRMonitorInfo* monitorInfos = data->ffXRRGetMonitors(data->display, RootWindowOfScreen(screen), True, &numberOfMonitors);
|
||||
if(monitorInfos == NULL)
|
||||
return false;
|
||||
|
||||
bool foundAMonitor;
|
||||
|
||||
for(int i = 0; i < numberOfMonitors; i++)
|
||||
{
|
||||
if(xrandrHandleMonitor(data, &monitorInfos[i]))
|
||||
foundAMonitor = true;
|
||||
}
|
||||
|
||||
data->ffXRRFreeMonitors(monitorInfos);
|
||||
|
||||
return foundAMonitor;
|
||||
}
|
||||
|
||||
static void xrandrHandleScreen(XrandrData* data, Screen* screen)
|
||||
{
|
||||
//Init screen configuration. This is used to get the default refresh rate. If this fails, default refresh rate is simply 0.
|
||||
XRRScreenConfiguration* screenConfiguration = data->ffXRRGetScreenInfo(data->display, RootWindowOfScreen(screen));
|
||||
|
||||
if(screenConfiguration != NULL)
|
||||
{
|
||||
data->defaultRefreshRate = (uint32_t) data->ffXRRConfigCurrentRate(screenConfiguration);
|
||||
data->ffXRRFreeScreenConfigInfo(screenConfiguration);
|
||||
}
|
||||
else
|
||||
data->defaultRefreshRate = 0;
|
||||
|
||||
//Init screen resources
|
||||
data->screenResources = data->ffXRRGetScreenResources(data->display, RootWindowOfScreen(screen));
|
||||
|
||||
bool ret = xrandrHandleMonitors(data, screen);
|
||||
|
||||
data->ffXRRFreeScreenResources(data->screenResources);
|
||||
|
||||
if(ret)
|
||||
return;
|
||||
|
||||
//Fallback to screen
|
||||
ffdsAppendResolution(
|
||||
data->result,
|
||||
(uint32_t) WidthOfScreen(screen),
|
||||
(uint32_t) HeightOfScreen(screen),
|
||||
data->defaultRefreshRate
|
||||
);
|
||||
}
|
||||
|
||||
void ffdsConnectXrandr(const FFinstance* instance, FFDisplayServerResult* result)
|
||||
{
|
||||
FF_LIBRARY_LOAD(xrandr, instance->config.libXrandr, , "libXrandr.so", 3)
|
||||
|
||||
FF_LIBRARY_LOAD_SYMBOL(xrandr, XOpenDisplay,)
|
||||
FF_LIBRARY_LOAD_SYMBOL(xrandr, XCloseDisplay,)
|
||||
|
||||
XrandrData data;
|
||||
|
||||
FF_LIBRARY_LOAD_SYMBOL_ADRESS(xrandr, data.ffXRRGetScreenInfo, XRRGetScreenInfo,)
|
||||
FF_LIBRARY_LOAD_SYMBOL_ADRESS(xrandr, data.ffXRRConfigCurrentRate, XRRConfigCurrentRate,);
|
||||
FF_LIBRARY_LOAD_SYMBOL_ADRESS(xrandr, data.ffXRRGetMonitors, XRRGetMonitors,);
|
||||
FF_LIBRARY_LOAD_SYMBOL_ADRESS(xrandr, data.ffXRRGetScreenResources, XRRGetScreenResources,);
|
||||
FF_LIBRARY_LOAD_SYMBOL_ADRESS(xrandr, data.ffXRRGetOutputInfo, XRRGetOutputInfo,);
|
||||
FF_LIBRARY_LOAD_SYMBOL_ADRESS(xrandr, data.ffXRRGetCrtcInfo, XRRGetCrtcInfo,);
|
||||
FF_LIBRARY_LOAD_SYMBOL_ADRESS(xrandr, data.ffXRRFreeCrtcInfo, XRRFreeCrtcInfo,);
|
||||
FF_LIBRARY_LOAD_SYMBOL_ADRESS(xrandr, data.ffXRRFreeOutputInfo, XRRFreeOutputInfo,);
|
||||
FF_LIBRARY_LOAD_SYMBOL_ADRESS(xrandr, data.ffXRRFreeScreenResources, XRRFreeScreenResources,);
|
||||
FF_LIBRARY_LOAD_SYMBOL_ADRESS(xrandr, data.ffXRRFreeMonitors, XRRFreeMonitors,);
|
||||
FF_LIBRARY_LOAD_SYMBOL_ADRESS(xrandr, data.ffXRRFreeScreenConfigInfo, XRRFreeScreenConfigInfo,);
|
||||
|
||||
X11PropertyData propertyData;
|
||||
bool propertyDataInitialized = x11InitPropertyData(xrandr, &propertyData);
|
||||
|
||||
data.display = ffXOpenDisplay(NULL);
|
||||
if(data.display == NULL)
|
||||
{
|
||||
dlclose(xrandr);
|
||||
return;
|
||||
}
|
||||
|
||||
if(propertyDataInitialized && ScreenCount(data.display) > 0)
|
||||
x11DetectWMFromEWMH(&propertyData, data.display, result);
|
||||
|
||||
data.result = result;
|
||||
|
||||
for(int i = 0; i < ScreenCount(data.display); i++)
|
||||
xrandrHandleScreen(&data, ScreenOfDisplay(data.display, i));
|
||||
|
||||
ffXCloseDisplay(data.display);
|
||||
dlclose(xrandr);
|
||||
|
||||
//If wayland hasn't set this, connection failed for it. So we are running only a X Server, not XWayland.
|
||||
if(result->wmProtocolName.length == 0)
|
||||
ffStrbufSetS(&result->wmProtocolName, FF_DISPLAYSERVER_PROTOCOL_X11);
|
||||
}
|
||||
|
||||
#else
|
||||
|
||||
void ffdsConnectXrandr(const FFinstance* instance, FFDisplayServerResult* result)
|
||||
{
|
||||
//Do nothing here. There are more x11 implementaions to come.
|
||||
FF_UNUSED(instance, result);
|
||||
}
|
||||
|
||||
#endif // FF_HAVE_XRANDR
|
||||
@@ -1,209 +0,0 @@
|
||||
#include "fastfetch.h"
|
||||
#include "detection/gtk.h"
|
||||
#include "common/properties.h"
|
||||
#include "common/settings.h"
|
||||
#include "detection/displayserver.h"
|
||||
|
||||
#include <pthread.h>
|
||||
|
||||
static inline bool allPropertiesSet(FFGTKResult* result)
|
||||
{
|
||||
return
|
||||
result->theme.length > 0 &&
|
||||
result->icons.length > 0 &&
|
||||
result->font.length > 0;
|
||||
}
|
||||
|
||||
static inline void applyGTKSettings(FFGTKResult* result, const char* themeName, const char* iconsName, const char* fontName, const char* cursorTheme, int cursorSize)
|
||||
{
|
||||
if(result->theme.length == 0)
|
||||
ffStrbufAppendS(&result->theme, themeName);
|
||||
|
||||
if(result->icons.length == 0)
|
||||
ffStrbufAppendS(&result->icons, iconsName);
|
||||
|
||||
if(result->font.length == 0)
|
||||
ffStrbufAppendS(&result->font, fontName);
|
||||
|
||||
if(result->cursor.length == 0)
|
||||
ffStrbufAppendS(&result->cursor, cursorTheme);
|
||||
|
||||
if(result->cursorSize.length == 0 && cursorSize > 0)
|
||||
ffStrbufAppendF(&result->cursorSize, "%i", cursorSize);
|
||||
}
|
||||
|
||||
static void detectGTKFromSettings(FFinstance* instance, FFGTKResult* result)
|
||||
{
|
||||
static pthread_mutex_t mutex = PTHREAD_MUTEX_INITIALIZER;
|
||||
|
||||
static const char* themeName = NULL;
|
||||
static const char* iconsName = NULL;
|
||||
static const char* fontName = NULL;
|
||||
static const char* cursorTheme = NULL;
|
||||
static int cursorSize = 0;
|
||||
|
||||
static bool init = false;
|
||||
|
||||
pthread_mutex_lock(&mutex);
|
||||
|
||||
if(init)
|
||||
{
|
||||
pthread_mutex_unlock(&mutex);
|
||||
applyGTKSettings(result, themeName, iconsName, fontName, cursorTheme, cursorSize);
|
||||
return;
|
||||
}
|
||||
|
||||
init = true;
|
||||
|
||||
const FFDisplayServerResult* wmde = ffConnectDisplayServer(instance);
|
||||
|
||||
if(ffStrbufIgnCaseCompS(&wmde->dePrettyName, "Xfce") == 0 || ffStrbufIgnCaseCompS(&wmde->dePrettyName, "Xfce4") == 0)
|
||||
{
|
||||
themeName = ffSettingsGetXFConf(instance, "xsettings", "/Net/ThemeName", FF_VARIANT_TYPE_STRING).strValue;
|
||||
iconsName = ffSettingsGetXFConf(instance, "xsettings", "/Net/IconThemeName", FF_VARIANT_TYPE_STRING).strValue;
|
||||
fontName = ffSettingsGetXFConf(instance, "xsettings", "/Gtk/FontName", FF_VARIANT_TYPE_STRING).strValue;
|
||||
cursorTheme = ffSettingsGetXFConf(instance, "xsettings", "/Gtk/CursorThemeName", FF_VARIANT_TYPE_STRING).strValue;
|
||||
cursorSize = ffSettingsGetXFConf(instance, "xsettings", "/Gtk/CursorThemeSize", FF_VARIANT_TYPE_INT).intValue;
|
||||
}
|
||||
else if(ffStrbufIgnCaseCompS(&wmde->dePrettyName, "Cinnamon") == 0)
|
||||
{
|
||||
themeName = ffSettingsGet(instance, "/org/cinnamon/desktop/interface/gtk-theme", "org.cinnamon.desktop.interface", NULL, "gtk-theme", FF_VARIANT_TYPE_STRING).strValue;
|
||||
iconsName = ffSettingsGet(instance, "/org/cinnamon/desktop/interface/icon-theme", "org.cinnamon.desktop.interface", NULL, "icon-theme", FF_VARIANT_TYPE_STRING).strValue;
|
||||
fontName = ffSettingsGet(instance, "/org/cinnamon/desktop/interface/font-name", "org.cinnamon.desktop.interface", NULL, "font-name", FF_VARIANT_TYPE_STRING).strValue;
|
||||
cursorTheme = ffSettingsGet(instance, "/org/cinnamon/desktop/interface/cursor-theme", "org.cinnamon.desktop.interface", NULL, "cursor-theme", FF_VARIANT_TYPE_STRING).strValue;
|
||||
cursorSize = ffSettingsGet(instance, "/org/cinnamon/desktop/interface/cursor-size", "org.cinnamon.desktop.interface", NULL, "cursor-size", FF_VARIANT_TYPE_INT).intValue;
|
||||
}
|
||||
else if(ffStrbufIgnCaseCompS(&wmde->dePrettyName, "Mate") == 0)
|
||||
{
|
||||
themeName = ffSettingsGet(instance, "/org/mate/interface/gtk-theme", "org.mate.interface", NULL, "gtk-theme", FF_VARIANT_TYPE_STRING).strValue;
|
||||
iconsName = ffSettingsGet(instance, "/org/mate/interface/icon-theme", "org.mate.interface", NULL, "icon-theme", FF_VARIANT_TYPE_STRING).strValue;
|
||||
fontName = ffSettingsGet(instance, "/org/mate/interface/font-name", "org.mate.interface", NULL, "font-name", FF_VARIANT_TYPE_STRING).strValue;
|
||||
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;
|
||||
}
|
||||
else if(ffStrbufIgnCaseCompS(&wmde->dePrettyName, "Gnome") == 0 || ffStrbufIgnCaseCompS(&wmde->dePrettyName, "Unity") == 0 || ffStrbufIgnCaseCompS(&wmde->dePrettyName, "Budgie") == 0)
|
||||
{
|
||||
themeName = ffSettingsGet(instance, "/org/gnome/desktop/interface/gtk-theme", "org.gnome.desktop.interface", NULL, "gtk-theme", FF_VARIANT_TYPE_STRING).strValue;
|
||||
iconsName = ffSettingsGet(instance, "/org/gnome/desktop/interface/icon-theme", "org.gnome.desktop.interface", NULL, "icon-theme", FF_VARIANT_TYPE_STRING).strValue;
|
||||
fontName = ffSettingsGet(instance, "/org/gnome/desktop/interface/font-name", "org.gnome.desktop.interface", NULL, "font-name", FF_VARIANT_TYPE_STRING).strValue;
|
||||
cursorTheme = ffSettingsGet(instance, "/org/gnome/desktop/interface/cursor-theme", "org.gnome.desktop.interface", NULL, "cursor-theme", FF_VARIANT_TYPE_STRING).strValue;
|
||||
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);
|
||||
applyGTKSettings(result, themeName, iconsName, fontName, cursorTheme, cursorSize);
|
||||
}
|
||||
|
||||
static void detectGTKFromConfigFile(const char* filename, FFGTKResult* result)
|
||||
{
|
||||
ffParsePropFileValues(filename, 5, (FFpropquery[]) {
|
||||
{"gtk-theme-name =", &result->theme},
|
||||
{"gtk-icon-theme-name =", &result->icons},
|
||||
{"gtk-font-name =", &result->font},
|
||||
{"gtk-cursor-theme-name =", &result->cursor},
|
||||
{"gtk-cursor-theme-size =", &result->cursorSize}
|
||||
});
|
||||
}
|
||||
|
||||
static void detectGTKFromConfigDir(FFstrbuf* configDir, const char* version, FFGTKResult* result)
|
||||
{
|
||||
//In case of an empty env variable
|
||||
ffStrbufTrim(configDir, ' ');
|
||||
if(configDir->length == 0)
|
||||
return;
|
||||
|
||||
uint32_t configDirLength = configDir->length;
|
||||
|
||||
// <configdir>/gtk-<version>.0/settings.ini
|
||||
ffStrbufAppendS(configDir, "gtk-");
|
||||
ffStrbufAppendS(configDir, version);
|
||||
ffStrbufAppendS(configDir, ".0/settings.ini");
|
||||
detectGTKFromConfigFile(configDir->chars, result);
|
||||
ffStrbufSubstrBefore(configDir, configDirLength);
|
||||
if(allPropertiesSet(result))
|
||||
return;
|
||||
|
||||
// <configdir>/gtk-<version>.0/gtkrc
|
||||
ffStrbufAppendS(configDir, "gtk-");
|
||||
ffStrbufAppendS(configDir, version);
|
||||
ffStrbufAppendS(configDir, ".0/gtkrc");
|
||||
detectGTKFromConfigFile(configDir->chars, result);
|
||||
ffStrbufSubstrBefore(configDir, configDirLength);
|
||||
if(allPropertiesSet(result))
|
||||
return;
|
||||
|
||||
// <configdir>/gtkrc-<version>.0
|
||||
ffStrbufAppendS(configDir, "gtkrc-");
|
||||
ffStrbufAppendS(configDir, version);
|
||||
ffStrbufAppendS(configDir, ".0");
|
||||
detectGTKFromConfigFile(configDir->chars, result);
|
||||
ffStrbufSubstrBefore(configDir, configDirLength);
|
||||
if(allPropertiesSet(result))
|
||||
return;
|
||||
|
||||
// <configdir>/.gtkrc-<version>.0
|
||||
ffStrbufAppendS(configDir, ".gtkrc-");
|
||||
ffStrbufAppendS(configDir, version);
|
||||
ffStrbufAppendS(configDir, ".0");
|
||||
detectGTKFromConfigFile(configDir->chars, result);
|
||||
ffStrbufSubstrBefore(configDir, configDirLength);
|
||||
}
|
||||
|
||||
static void detectGTK(FFinstance* instance, const char* version, FFGTKResult* result)
|
||||
{
|
||||
//Mate, Cinnamon and Gnome use dconf to save theme config
|
||||
//On other DEs, this will do nothing
|
||||
detectGTKFromSettings(instance, result);
|
||||
if(allPropertiesSet(result))
|
||||
return;
|
||||
|
||||
//We need to do this because we use multiple threads on configDirs
|
||||
FFstrbuf baseDir;
|
||||
ffStrbufInitA(&baseDir, 64);
|
||||
|
||||
for(uint32_t i = 0; i < instance->state.configDirs.length; i++)
|
||||
{
|
||||
ffStrbufSet(&baseDir, ffListGet(&instance->state.configDirs, i));
|
||||
detectGTKFromConfigDir(&baseDir, version, result);
|
||||
if(allPropertiesSet(result))
|
||||
break;
|
||||
}
|
||||
|
||||
ffStrbufDestroy(&baseDir);
|
||||
}
|
||||
|
||||
#define FF_DETECT_GTK_IMPL(version) \
|
||||
static pthread_mutex_t mutex = PTHREAD_MUTEX_INITIALIZER; \
|
||||
static FFGTKResult result; \
|
||||
static bool init = false; \
|
||||
pthread_mutex_lock(&mutex); \
|
||||
if(init){ \
|
||||
pthread_mutex_unlock(&mutex);\
|
||||
return &result; \
|
||||
} \
|
||||
init = true; \
|
||||
ffStrbufInit(&result.theme); \
|
||||
ffStrbufInit(&result.icons); \
|
||||
ffStrbufInit(&result.font); \
|
||||
ffStrbufInit(&result.cursor); \
|
||||
ffStrbufInit(&result.cursorSize); \
|
||||
detectGTK(instance, #version, &result); \
|
||||
pthread_mutex_unlock(&mutex); \
|
||||
return &result;
|
||||
|
||||
const FFGTKResult* ffDetectGTK2(FFinstance* instance)
|
||||
{
|
||||
FF_DETECT_GTK_IMPL(2)
|
||||
}
|
||||
|
||||
const FFGTKResult* ffDetectGTK3(FFinstance* instance)
|
||||
{
|
||||
FF_DETECT_GTK_IMPL(3)
|
||||
}
|
||||
|
||||
const FFGTKResult* ffDetectGTK4(FFinstance* instance)
|
||||
{
|
||||
FF_DETECT_GTK_IMPL(4)
|
||||
}
|
||||
|
||||
#undef FF_CALCULATE_GTK_IMPL
|
||||
@@ -1,21 +0,0 @@
|
||||
#pragma once
|
||||
|
||||
#ifndef FF_INCLUDED_detection_gtk
|
||||
#define FF_INCLUDED_detection_gtk
|
||||
|
||||
#include "fastfetch.h"
|
||||
|
||||
typedef struct FFGTKResult
|
||||
{
|
||||
FFstrbuf theme;
|
||||
FFstrbuf icons;
|
||||
FFstrbuf font;
|
||||
FFstrbuf cursor;
|
||||
FFstrbuf cursorSize;
|
||||
} FFGTKResult;
|
||||
|
||||
const FFGTKResult* ffDetectGTK2(FFinstance* instance);
|
||||
const FFGTKResult* ffDetectGTK4(FFinstance* instance);
|
||||
const FFGTKResult* ffDetectGTK3(FFinstance* instance);
|
||||
|
||||
#endif
|
||||
@@ -1,370 +0,0 @@
|
||||
#include "fastfetch.h"
|
||||
#include "detection/media.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 "common/library.h"
|
||||
#include "common/parsing.h"
|
||||
#include <dbus/dbus.h>
|
||||
|
||||
#define FF_DBUS_ITER_CONTINUE(iterator) \
|
||||
{ \
|
||||
if(!data->ffdbus_message_iter_has_next(&iterator)) \
|
||||
break; \
|
||||
data->ffdbus_message_iter_next(&iterator); \
|
||||
continue; \
|
||||
}
|
||||
|
||||
typedef struct DBusData
|
||||
{
|
||||
FF_LIBRARY_SYMBOL(dbus_message_new_method_call)
|
||||
FF_LIBRARY_SYMBOL(dbus_message_iter_init)
|
||||
FF_LIBRARY_SYMBOL(dbus_message_iter_init_append)
|
||||
FF_LIBRARY_SYMBOL(dbus_message_iter_append_basic)
|
||||
FF_LIBRARY_SYMBOL(dbus_message_iter_get_arg_type)
|
||||
FF_LIBRARY_SYMBOL(dbus_message_iter_get_basic)
|
||||
FF_LIBRARY_SYMBOL(dbus_message_iter_recurse)
|
||||
FF_LIBRARY_SYMBOL(dbus_message_iter_has_next)
|
||||
FF_LIBRARY_SYMBOL(dbus_message_iter_next)
|
||||
FF_LIBRARY_SYMBOL(dbus_message_unref)
|
||||
FF_LIBRARY_SYMBOL(dbus_connection_send_with_reply)
|
||||
FF_LIBRARY_SYMBOL(dbus_connection_flush)
|
||||
FF_LIBRARY_SYMBOL(dbus_pending_call_block)
|
||||
FF_LIBRARY_SYMBOL(dbus_pending_call_steal_reply)
|
||||
FF_LIBRARY_SYMBOL(dbus_pending_call_unref)
|
||||
|
||||
DBusConnection *connection;
|
||||
} DBusData;
|
||||
|
||||
static bool getValue(DBusMessageIter* iter, FFstrbuf* result, DBusData* data)
|
||||
{
|
||||
int argType = data->ffdbus_message_iter_get_arg_type(iter);
|
||||
|
||||
if(argType == DBUS_TYPE_STRING)
|
||||
{
|
||||
const char* value = NULL;
|
||||
data->ffdbus_message_iter_get_basic(iter, &value);
|
||||
|
||||
if(!ffStrSet(value))
|
||||
return false;
|
||||
|
||||
ffStrbufAppendS(result, value);
|
||||
return true;
|
||||
}
|
||||
|
||||
if(argType != DBUS_TYPE_VARIANT && argType != DBUS_TYPE_ARRAY)
|
||||
return false;
|
||||
|
||||
DBusMessageIter subIter;
|
||||
data->ffdbus_message_iter_recurse(iter, &subIter);
|
||||
|
||||
if(argType == DBUS_TYPE_VARIANT)
|
||||
return getValue(&subIter, result, data);
|
||||
|
||||
//At this point we have an array
|
||||
|
||||
bool foundAValue = false;
|
||||
|
||||
while(true)
|
||||
{
|
||||
if(getValue(&subIter, result, data))
|
||||
{
|
||||
foundAValue = true;
|
||||
ffStrbufAppendS(result, ", ");
|
||||
}
|
||||
|
||||
FF_DBUS_ITER_CONTINUE(subIter);
|
||||
}
|
||||
|
||||
if(foundAValue)
|
||||
ffStrbufSubstrBefore(result, result->length - 2);
|
||||
|
||||
return foundAValue;
|
||||
}
|
||||
|
||||
static DBusMessage* getReply(DBusMessage* request, DBusData* data)
|
||||
{
|
||||
DBusPendingCall* pendingCall = NULL;
|
||||
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;
|
||||
|
||||
data->ffdbus_connection_flush(data->connection);
|
||||
data->ffdbus_pending_call_block(pendingCall);
|
||||
|
||||
DBusMessage* reply = data->ffdbus_pending_call_steal_reply(pendingCall);
|
||||
data->ffdbus_pending_call_unref(pendingCall);
|
||||
return reply;
|
||||
}
|
||||
|
||||
static DBusMessage* getProperty(const char* busName, const char* objectPath, const char* interface, const char* property, DBusData* data)
|
||||
{
|
||||
DBusMessage* message = data->ffdbus_message_new_method_call(busName, objectPath, "org.freedesktop.DBus.Properties", "Get");
|
||||
if(message == NULL)
|
||||
return NULL;
|
||||
|
||||
DBusMessageIter requestIterator;
|
||||
data->ffdbus_message_iter_init_append(message, &requestIterator);
|
||||
|
||||
if(!data->ffdbus_message_iter_append_basic(&requestIterator, DBUS_TYPE_STRING, &interface))
|
||||
{
|
||||
data->ffdbus_message_unref(message);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
if(!data->ffdbus_message_iter_append_basic(&requestIterator, DBUS_TYPE_STRING, &property))
|
||||
{
|
||||
data->ffdbus_message_unref(message);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
return getReply(message, data);
|
||||
}
|
||||
|
||||
static void getPropertyString(const char* busName, const char* objectPath, const char* interface, const char* property, FFstrbuf* result, DBusData* data)
|
||||
{
|
||||
DBusMessage* reply = getProperty(busName, objectPath, interface, property, data);
|
||||
if(reply == NULL)
|
||||
return;
|
||||
|
||||
DBusMessageIter rootIterator;
|
||||
if(!data->ffdbus_message_iter_init(reply, &rootIterator))
|
||||
{
|
||||
data->ffdbus_message_unref(reply);
|
||||
return;
|
||||
}
|
||||
|
||||
getValue(&rootIterator, result, data);
|
||||
|
||||
data->ffdbus_message_unref(reply);
|
||||
}
|
||||
|
||||
static bool getBusProperties(const char* busName, FFMediaResult* result, DBusData* data)
|
||||
{
|
||||
DBusMessage* reply = getProperty(busName, "/org/mpris/MediaPlayer2", "org.mpris.MediaPlayer2.Player", "Metadata", data);
|
||||
if(reply == NULL)
|
||||
return false;
|
||||
|
||||
DBusMessageIter rootIterator;
|
||||
if(!data->ffdbus_message_iter_init(reply, &rootIterator))
|
||||
{
|
||||
data->ffdbus_message_unref(reply);
|
||||
return false;
|
||||
}
|
||||
|
||||
if(data->ffdbus_message_iter_get_arg_type(&rootIterator) != DBUS_TYPE_VARIANT)
|
||||
{
|
||||
data->ffdbus_message_unref(reply);
|
||||
return false;
|
||||
}
|
||||
|
||||
DBusMessageIter variantIterator;
|
||||
data->ffdbus_message_iter_recurse(&rootIterator, &variantIterator);
|
||||
if(data->ffdbus_message_iter_get_arg_type(&variantIterator) != DBUS_TYPE_ARRAY)
|
||||
{
|
||||
data->ffdbus_message_unref(reply);
|
||||
return false;
|
||||
}
|
||||
|
||||
DBusMessageIter arrayIterator;
|
||||
data->ffdbus_message_iter_recurse(&variantIterator, &arrayIterator);
|
||||
|
||||
while(true)
|
||||
{
|
||||
if(data->ffdbus_message_iter_get_arg_type(&arrayIterator) != DBUS_TYPE_DICT_ENTRY)
|
||||
FF_DBUS_ITER_CONTINUE(arrayIterator)
|
||||
|
||||
DBusMessageIter dictIterator;
|
||||
data->ffdbus_message_iter_recurse(&arrayIterator, &dictIterator);
|
||||
|
||||
if(data->ffdbus_message_iter_get_arg_type(&dictIterator) != DBUS_TYPE_STRING)
|
||||
FF_DBUS_ITER_CONTINUE(arrayIterator)
|
||||
|
||||
if(!data->ffdbus_message_iter_has_next(&dictIterator))
|
||||
FF_DBUS_ITER_CONTINUE(arrayIterator)
|
||||
|
||||
const char* key;
|
||||
data->ffdbus_message_iter_get_basic(&dictIterator, &key);
|
||||
|
||||
data->ffdbus_message_iter_next(&dictIterator);
|
||||
|
||||
if(strcmp(key, "xesam:title") == 0)
|
||||
getValue(&dictIterator, &result->song, data);
|
||||
else if(strcmp(key, "xesam:album") == 0)
|
||||
getValue(&dictIterator, &result->album, data);
|
||||
else if(strcmp(key, "xesam:artist") == 0)
|
||||
getValue(&dictIterator, &result->artist, data);
|
||||
else if(strcmp(key, "xesam:url") == 0)
|
||||
getValue(&dictIterator, &result->url, data);
|
||||
|
||||
if(result->song.length > 0 && result->artist.length > 0 && result->album.length > 0)
|
||||
break;
|
||||
|
||||
FF_DBUS_ITER_CONTINUE(arrayIterator)
|
||||
}
|
||||
|
||||
data->ffdbus_message_unref(reply);
|
||||
|
||||
if(result->song.length == 0)
|
||||
{
|
||||
ffStrbufClear(&result->artist);
|
||||
ffStrbufClear(&result->album);
|
||||
ffStrbufClear(&result->url);
|
||||
return false;
|
||||
}
|
||||
|
||||
//We found a song, get the player name
|
||||
|
||||
getPropertyString(busName, "/org/mpris/MediaPlayer2", "org.mpris.MediaPlayer2", "Identity", &result->player, data);
|
||||
|
||||
if(result->player.length == 0)
|
||||
getPropertyString(busName, "/org/mpris/MediaPlayer2", "org.mpris.MediaPlayer2", "DesktopEntry", &result->player, data);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
static void getCustomBus(FFinstance* instance, FFMediaResult* result, DBusData* data)
|
||||
{
|
||||
if(ffStrbufStartsWithS(&instance->config.playerName, FF_DBUS_MPRIS_PREFIX))
|
||||
{
|
||||
ffStrbufAppendS(&result->busNameShort, instance->config.playerName.chars + sizeof(FF_DBUS_MPRIS_PREFIX) - 1);
|
||||
getBusProperties(instance->config.playerName.chars, result, data);
|
||||
return;
|
||||
}
|
||||
|
||||
ffStrbufAppend(&result->busNameShort, &instance->config.playerName);
|
||||
|
||||
FFstrbuf busName;
|
||||
ffStrbufInit(&busName);
|
||||
ffStrbufAppendS(&busName, FF_DBUS_MPRIS_PREFIX);
|
||||
ffStrbufAppend(&busName, &instance->config.playerName);
|
||||
getBusProperties(busName.chars, result, data);
|
||||
ffStrbufDestroy(&busName);
|
||||
}
|
||||
|
||||
static void getBestBus(FFMediaResult* result, DBusData* data)
|
||||
{
|
||||
DBusMessage* message = data->ffdbus_message_new_method_call("org.freedesktop.DBus", "/org/freedesktop/DBus", "org.freedesktop.DBus", "ListNames");
|
||||
if(message == NULL)
|
||||
return;
|
||||
|
||||
DBusMessage* reply = getReply(message, data);
|
||||
if(reply == NULL)
|
||||
return;
|
||||
|
||||
DBusMessageIter rootIterator;
|
||||
if(!data->ffdbus_message_iter_init(reply, &rootIterator) || data->ffdbus_message_iter_get_arg_type(&rootIterator) != DBUS_TYPE_ARRAY)
|
||||
return;
|
||||
|
||||
DBusMessageIter arrayIterator;
|
||||
data->ffdbus_message_iter_recurse(&rootIterator, &arrayIterator);
|
||||
|
||||
while(true)
|
||||
{
|
||||
if(data->ffdbus_message_iter_get_arg_type(&arrayIterator) != DBUS_TYPE_STRING)
|
||||
FF_DBUS_ITER_CONTINUE(arrayIterator)
|
||||
|
||||
const char* busName;
|
||||
data->ffdbus_message_iter_get_basic(&arrayIterator, &busName);
|
||||
|
||||
if(strncmp(busName, FF_DBUS_MPRIS_PREFIX, sizeof(FF_DBUS_MPRIS_PREFIX) - 1) != 0)
|
||||
FF_DBUS_ITER_CONTINUE(arrayIterator)
|
||||
|
||||
if(getBusProperties(busName, result, data))
|
||||
{
|
||||
ffStrbufAppendS(&result->busNameShort, busName + sizeof(FF_DBUS_MPRIS_PREFIX) - 1);
|
||||
break;
|
||||
}
|
||||
|
||||
FF_DBUS_ITER_CONTINUE(arrayIterator)
|
||||
}
|
||||
|
||||
data->ffdbus_message_unref(reply);
|
||||
}
|
||||
|
||||
static void getMedia(FFinstance* instance, FFMediaResult* result)
|
||||
{
|
||||
DBusData data;
|
||||
|
||||
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,)
|
||||
FF_LIBRARY_LOAD_SYMBOL_ADRESS(dbus, data.ffdbus_message_iter_init_append, dbus_message_iter_init_append,)
|
||||
FF_LIBRARY_LOAD_SYMBOL_ADRESS(dbus, data.ffdbus_message_iter_append_basic, dbus_message_iter_append_basic,)
|
||||
FF_LIBRARY_LOAD_SYMBOL_ADRESS(dbus, data.ffdbus_message_iter_get_arg_type, dbus_message_iter_get_arg_type,)
|
||||
FF_LIBRARY_LOAD_SYMBOL_ADRESS(dbus, data.ffdbus_message_iter_get_basic, dbus_message_iter_get_basic,)
|
||||
FF_LIBRARY_LOAD_SYMBOL_ADRESS(dbus, data.ffdbus_message_iter_recurse, dbus_message_iter_recurse,)
|
||||
FF_LIBRARY_LOAD_SYMBOL_ADRESS(dbus, data.ffdbus_message_iter_has_next, dbus_message_iter_has_next,)
|
||||
FF_LIBRARY_LOAD_SYMBOL_ADRESS(dbus, data.ffdbus_message_iter_next, dbus_message_iter_next,)
|
||||
FF_LIBRARY_LOAD_SYMBOL_ADRESS(dbus, data.ffdbus_message_unref, dbus_message_unref,)
|
||||
FF_LIBRARY_LOAD_SYMBOL_ADRESS(dbus, data.ffdbus_connection_send_with_reply, dbus_connection_send_with_reply,)
|
||||
FF_LIBRARY_LOAD_SYMBOL_ADRESS(dbus, data.ffdbus_connection_flush, dbus_connection_flush,)
|
||||
FF_LIBRARY_LOAD_SYMBOL_ADRESS(dbus, data.ffdbus_pending_call_block, dbus_pending_call_block,)
|
||||
FF_LIBRARY_LOAD_SYMBOL_ADRESS(dbus, data.ffdbus_pending_call_steal_reply, dbus_pending_call_steal_reply,)
|
||||
FF_LIBRARY_LOAD_SYMBOL_ADRESS(dbus, data.ffdbus_pending_call_unref, dbus_pending_call_unref,)
|
||||
|
||||
data.connection = ffdbus_bus_get(DBUS_BUS_SESSION, NULL);
|
||||
if(data.connection == NULL)
|
||||
{
|
||||
dlclose(dbus);
|
||||
return;
|
||||
}
|
||||
|
||||
if(instance->config.playerName.length > 0)
|
||||
getCustomBus(instance, result, &data);
|
||||
else
|
||||
getBestBus(result, &data);
|
||||
|
||||
dlclose(dbus);
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
const FFMediaResult* ffDetectMedia(FFinstance* instance)
|
||||
{
|
||||
static FFMediaResult result;
|
||||
static pthread_mutex_t mutex = PTHREAD_MUTEX_INITIALIZER;
|
||||
static bool init = false;
|
||||
|
||||
pthread_mutex_lock(&mutex);
|
||||
if(init)
|
||||
{
|
||||
pthread_mutex_unlock(&mutex);
|
||||
return &result;
|
||||
}
|
||||
init = true;
|
||||
|
||||
ffStrbufInit(&result.busNameShort);
|
||||
ffStrbufInit(&result.player);
|
||||
ffStrbufInit(&result.song);
|
||||
ffStrbufInit(&result.artist);
|
||||
ffStrbufInit(&result.album);
|
||||
ffStrbufInit(&result.url);
|
||||
|
||||
#ifdef FF_HAVE_DBUS
|
||||
getMedia(instance, &result);
|
||||
#endif
|
||||
|
||||
//Set busNameShort if a custom player was given, but loading dbus failed
|
||||
if(instance->config.playerName.length > 0 && result.busNameShort.length == 0)
|
||||
{
|
||||
if(ffStrbufStartsWithS(&instance->config.playerName, FF_DBUS_MPRIS_PREFIX))
|
||||
ffStrbufAppendS(&result.busNameShort, instance->config.playerName.chars + sizeof(FF_DBUS_MPRIS_PREFIX) - 1);
|
||||
else
|
||||
ffStrbufAppend(&result.busNameShort, &instance->config.playerName);
|
||||
}
|
||||
|
||||
//Set player to busNameShort, if detection failed
|
||||
if(result.player.length == 0)
|
||||
ffStrbufAppend(&result.player, &result.busNameShort);
|
||||
|
||||
pthread_mutex_unlock(&mutex);
|
||||
return &result;
|
||||
}
|
||||
@@ -1,20 +0,0 @@
|
||||
#pragma once
|
||||
|
||||
#ifndef FF_INCLUDED_detection_media
|
||||
#define FF_INCLUDED_detection_media
|
||||
|
||||
#include "fastfetch.h"
|
||||
|
||||
typedef struct FFMediaResult
|
||||
{
|
||||
FFstrbuf busNameShort; //e.g. plasma-browser-integration
|
||||
FFstrbuf player; // e.g. Google Chrome
|
||||
FFstrbuf song;
|
||||
FFstrbuf artist;
|
||||
FFstrbuf album;
|
||||
FFstrbuf url;
|
||||
} FFMediaResult;
|
||||
|
||||
const FFMediaResult* ffDetectMedia(FFinstance* instance);
|
||||
|
||||
#endif
|
||||
@@ -1,166 +0,0 @@
|
||||
#include "fastfetch.h"
|
||||
#include "detection/os.h"
|
||||
#include "common/properties.h"
|
||||
#include "common/parsing.h"
|
||||
|
||||
#include <string.h>
|
||||
#include <stdlib.h>
|
||||
#include <pthread.h>
|
||||
|
||||
#if __ANDROID__
|
||||
#include "common/settings.h"
|
||||
#else
|
||||
|
||||
static bool parseFile(const char* fileName, FFOSResult* result)
|
||||
{
|
||||
if(result->id.length > 0 && result->name.length > 0 && result->prettyName.length > 0)
|
||||
return true;
|
||||
|
||||
return ffParsePropFileValues(fileName, 13, (FFpropquery[]) {
|
||||
{"NAME =", &result->name},
|
||||
{"DISTRIB_DESCRIPTION =", &result->prettyName},
|
||||
{"PRETTY_NAME =", &result->prettyName},
|
||||
{"DISTRIB_ID =", &result->id},
|
||||
{"ID =", &result->id},
|
||||
{"ID_LIKE =", &result->idLike},
|
||||
{"VARIANT =", &result->variant},
|
||||
{"VARIANT_ID =", &result->variantID},
|
||||
{"DISTRIB_RELEASE =", &result->version},
|
||||
{"VERSION =", &result->version},
|
||||
{"VERSION_ID =", &result->versionID},
|
||||
{"VERSION_CODENAME =", &result->codename},
|
||||
{"BUILD_ID =", &result->buildID}
|
||||
});
|
||||
}
|
||||
|
||||
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(const FFinstance* instance)
|
||||
{
|
||||
static pthread_mutex_t mutex = PTHREAD_MUTEX_INITIALIZER;
|
||||
static FFOSResult result;
|
||||
static bool init = false;
|
||||
pthread_mutex_lock(&mutex);
|
||||
if(init)
|
||||
{
|
||||
pthread_mutex_unlock(&mutex);
|
||||
return &result;
|
||||
}
|
||||
init = true;
|
||||
|
||||
ffStrbufInit(&result.systemName);
|
||||
ffStrbufInit(&result.name);
|
||||
ffStrbufInit(&result.prettyName);
|
||||
ffStrbufInit(&result.id);
|
||||
ffStrbufInit(&result.idLike);
|
||||
ffStrbufInit(&result.variant);
|
||||
ffStrbufInit(&result.variantID);
|
||||
ffStrbufInit(&result.version);
|
||||
ffStrbufInit(&result.versionID);
|
||||
ffStrbufInit(&result.codename);
|
||||
ffStrbufInit(&result.buildID);
|
||||
ffStrbufInit(&result.architecture);
|
||||
|
||||
ffStrbufSetS(&result.systemName, instance->state.utsname.sysname);
|
||||
ffStrbufSetS(&result.architecture, instance->state.utsname.machine);
|
||||
|
||||
#if !__ANDROID__
|
||||
if(instance->config.osFile.length > 0)
|
||||
{
|
||||
parseFile(instance->config.osFile.chars, &result);
|
||||
}
|
||||
else if(instance->config.escapeBedrock && parseFile(FASTFETCH_TARGET_DIR_ROOT"/bedrock/etc/bedrock-release", &result))
|
||||
{
|
||||
if(result.id.length == 0)
|
||||
ffStrbufAppendS(&result.id, "bedrock");
|
||||
|
||||
if(result.name.length == 0)
|
||||
ffStrbufAppendS(&result.name, "Bedrock");
|
||||
|
||||
if(result.prettyName.length == 0)
|
||||
ffStrbufAppendS(&result.prettyName, "Bedrock Linux");
|
||||
}
|
||||
else
|
||||
{
|
||||
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");
|
||||
ffSettingsGetAndroidProperty("ro.build.version.release", &result.versionID);
|
||||
ffSettingsGetAndroidProperty("ro.build.version.release", &result.version);
|
||||
ffSettingsGetAndroidProperty("ro.build.version.codename", &result.codename);
|
||||
ffSettingsGetAndroidProperty("ro.build.id", &result.buildID);
|
||||
#endif
|
||||
|
||||
pthread_mutex_unlock(&mutex);
|
||||
|
||||
return &result;
|
||||
}
|
||||
@@ -1,26 +0,0 @@
|
||||
#pragma once
|
||||
|
||||
#ifndef FF_INCLUDED_detection_os
|
||||
#define FF_INCLUDED_detection_os
|
||||
|
||||
#include "fastfetch.h"
|
||||
|
||||
typedef struct FFOSResult
|
||||
{
|
||||
FFstrbuf systemName;
|
||||
FFstrbuf name;
|
||||
FFstrbuf prettyName;
|
||||
FFstrbuf id;
|
||||
FFstrbuf idLike;
|
||||
FFstrbuf variant;
|
||||
FFstrbuf variantID;
|
||||
FFstrbuf version;
|
||||
FFstrbuf versionID;
|
||||
FFstrbuf codename;
|
||||
FFstrbuf buildID;
|
||||
FFstrbuf architecture;
|
||||
} FFOSResult;
|
||||
|
||||
const FFOSResult* ffDetectOS(const FFinstance* instance);
|
||||
|
||||
#endif
|
||||
@@ -1,162 +0,0 @@
|
||||
#include "fastfetch.h"
|
||||
#include "detection/qt.h"
|
||||
#include "common/properties.h"
|
||||
#include "detection/displayserver.h"
|
||||
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <pthread.h>
|
||||
|
||||
static inline bool allValuesSet(const FFQtResult* result)
|
||||
{
|
||||
return
|
||||
result->widgetStyle.length > 0 &&
|
||||
result->colorScheme.length > 0 &&
|
||||
result->icons.length > 0 &&
|
||||
result->font.length > 0;
|
||||
}
|
||||
|
||||
typedef enum PlasmaCategory
|
||||
{
|
||||
PLASMA_CATEGORY_GENERAL,
|
||||
PLASMA_CATEGORY_KDE,
|
||||
PLASMA_CATEGORY_ICONS,
|
||||
PLASMA_CATEGORY_OTHER
|
||||
} PlasmaCategory;
|
||||
|
||||
static bool detectPlasmaFromFile(const char* filename, FFQtResult* result)
|
||||
{
|
||||
FILE* kdeglobals = fopen(filename, "r");
|
||||
if(kdeglobals == NULL)
|
||||
return false;
|
||||
|
||||
char* line = NULL;
|
||||
size_t len = 0;
|
||||
|
||||
PlasmaCategory category = PLASMA_CATEGORY_OTHER;
|
||||
|
||||
while(getline(&line, &len, kdeglobals) != -1)
|
||||
{
|
||||
if(line[0] == '[')
|
||||
{
|
||||
char categoryName[32];
|
||||
sscanf(line, "[%31[^]]", categoryName);
|
||||
|
||||
if(strcasecmp(categoryName, "General") == 0)
|
||||
category = PLASMA_CATEGORY_GENERAL;
|
||||
else if(strcasecmp(categoryName, "KDE") == 0)
|
||||
category = PLASMA_CATEGORY_KDE;
|
||||
else if(strcasecmp(categoryName, "Icons") == 0)
|
||||
category = PLASMA_CATEGORY_ICONS;
|
||||
else
|
||||
category = PLASMA_CATEGORY_OTHER;
|
||||
|
||||
continue;
|
||||
}
|
||||
|
||||
if(category == PLASMA_CATEGORY_KDE && result->widgetStyle.length == 0)
|
||||
ffParsePropLine(line, "widgetStyle =", &result->widgetStyle);
|
||||
else if(category == PLASMA_CATEGORY_ICONS && result->icons.length == 0)
|
||||
ffParsePropLine(line, "Theme =", &result->icons);
|
||||
else if(category == PLASMA_CATEGORY_GENERAL)
|
||||
{
|
||||
if(result->colorScheme.length == 0)
|
||||
ffParsePropLine(line, "ColorScheme =", &result->colorScheme);
|
||||
|
||||
if(result->font.length == 0)
|
||||
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)
|
||||
ffParsePropLine(line, "Font =", &result->font);
|
||||
}
|
||||
}
|
||||
|
||||
if(line != NULL)
|
||||
free(line);
|
||||
|
||||
fclose(kdeglobals);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
static void detectPlasma(const FFinstance* instance, FFQtResult* result)
|
||||
{
|
||||
bool foundAFile = false;
|
||||
|
||||
//We need to do this because we use multiple threads on configDirs
|
||||
FFstrbuf baseDir;
|
||||
ffStrbufInitA(&baseDir, 64);
|
||||
|
||||
for(uint32_t i = 0; i < instance->state.configDirs.length; i++)
|
||||
{
|
||||
ffStrbufSet(&baseDir, ffListGet(&instance->state.configDirs, i));
|
||||
ffStrbufAppendS(&baseDir, "kdeglobals");
|
||||
|
||||
if(detectPlasmaFromFile(baseDir.chars, result))
|
||||
foundAFile = true;
|
||||
|
||||
if(allValuesSet(result))
|
||||
{
|
||||
ffStrbufDestroy(&baseDir);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
ffStrbufDestroy(&baseDir);
|
||||
if(!foundAFile)
|
||||
return;
|
||||
|
||||
//In Plasma the default value is never set in the config file, but the whole key-value is discarded.
|
||||
///We must set these values by our self if the file exists (it always does here)
|
||||
if(result->widgetStyle.length == 0)
|
||||
ffStrbufAppendS(&result->widgetStyle, "Breeze");
|
||||
|
||||
if(result->colorScheme.length == 0)
|
||||
ffStrbufAppendS(&result->colorScheme, "BreezeLight");
|
||||
|
||||
if(result->icons.length == 0)
|
||||
ffStrbufAppendS(&result->icons, "Breeze");
|
||||
|
||||
if(result->font.length == 0)
|
||||
ffStrbufAppendS(&result->font, "Noto Sans, 10");
|
||||
}
|
||||
|
||||
static void detectLXQt(const FFinstance* instance, FFQtResult* result)
|
||||
{
|
||||
ffParsePropFileConfigValues(instance, "lxqt/lxqt.conf", 3, (FFpropquery[]) {
|
||||
{"style = ", &result->widgetStyle},
|
||||
{"icon_theme = ", &result->icons},
|
||||
{"font = ", &result->font}
|
||||
});
|
||||
}
|
||||
|
||||
const FFQtResult* ffDetectQt(FFinstance* instance)
|
||||
{
|
||||
static FFQtResult result;
|
||||
|
||||
static pthread_mutex_t mutex = PTHREAD_MUTEX_INITIALIZER;
|
||||
static bool init = false;
|
||||
pthread_mutex_lock(&mutex);
|
||||
if(init)
|
||||
{
|
||||
pthread_mutex_unlock(&mutex);
|
||||
return &result;
|
||||
}
|
||||
init = true;
|
||||
|
||||
ffStrbufInit(&result.widgetStyle);
|
||||
ffStrbufInit(&result.colorScheme);
|
||||
ffStrbufInit(&result.icons);
|
||||
ffStrbufInit(&result.font);
|
||||
|
||||
const FFDisplayServerResult* wmde = ffConnectDisplayServer(instance);
|
||||
|
||||
if(ffStrbufIgnCaseCompS(&wmde->dePrettyName, "KDE Plasma") == 0)
|
||||
detectPlasma(instance, &result);
|
||||
else if(ffStrbufIgnCaseCompS(&wmde->dePrettyName, "LXQt") == 0)
|
||||
detectLXQt(instance, &result);
|
||||
|
||||
pthread_mutex_unlock(&mutex);
|
||||
return &result;
|
||||
}
|
||||
@@ -1,18 +0,0 @@
|
||||
#pragma once
|
||||
|
||||
#ifndef FF_INCLUDED_detection_qt
|
||||
#define FF_INCLUDED_detection_qt
|
||||
|
||||
#include "fastfetch.h"
|
||||
|
||||
typedef struct FFQtResult
|
||||
{
|
||||
FFstrbuf widgetStyle;
|
||||
FFstrbuf colorScheme;
|
||||
FFstrbuf icons;
|
||||
FFstrbuf font;
|
||||
} FFQtResult;
|
||||
|
||||
const FFQtResult* ffDetectQt(FFinstance* instance);
|
||||
|
||||
#endif
|
||||
@@ -1,121 +0,0 @@
|
||||
#include "fastfetch.h"
|
||||
#include "detection/temps.h"
|
||||
#include "common/io.h"
|
||||
|
||||
#include <string.h>
|
||||
#include <pthread.h>
|
||||
#include <dirent.h>
|
||||
|
||||
static bool isTempFile(const char* name)
|
||||
{
|
||||
return
|
||||
strncmp(name, "temp", 4) == 0 &&
|
||||
name[4] >= '0' &&
|
||||
name[4] <= '9' &&
|
||||
strncmp(name + 5, "_input", 6) == 0;
|
||||
}
|
||||
|
||||
static bool parseHwmonDir(FFstrbuf* dir, FFTempValue* value)
|
||||
{
|
||||
DIR* dirp = opendir(dir->chars);
|
||||
if(dirp == NULL)
|
||||
return false;
|
||||
|
||||
uint32_t dirLength = dir->length;
|
||||
value->value = 0.0 / 0.0; //use NaN as error value
|
||||
|
||||
FFstrbuf valueString;
|
||||
ffStrbufInit(&valueString);
|
||||
|
||||
struct dirent* dirent;
|
||||
while((dirent = readdir(dirp)) != NULL)
|
||||
{
|
||||
if(!isTempFile(dirent->d_name))
|
||||
continue;
|
||||
|
||||
ffStrbufAppendS(dir, dirent->d_name);
|
||||
ffReadFileBuffer(dir->chars, &valueString);
|
||||
ffStrbufSubstrBefore(dir, dirLength);
|
||||
|
||||
//ffStrbufToDouble() returns NaN if the string couldn't be parsed
|
||||
value->value = ffStrbufToDouble(&valueString);
|
||||
if(value->value != value->value)
|
||||
continue;
|
||||
|
||||
value->value /= 1000.0; //millidegrees to degrees
|
||||
break;
|
||||
}
|
||||
|
||||
closedir(dirp);
|
||||
ffStrbufDestroy(&valueString);
|
||||
|
||||
if(value->value != value->value)
|
||||
return false;
|
||||
|
||||
ffStrbufAppendS(dir, "name");
|
||||
ffReadFileBuffer(dir->chars, &value->name);
|
||||
ffStrbufSubstrBefore(dir, dirLength);
|
||||
|
||||
ffStrbufAppendS(dir, "device/class");
|
||||
ffReadFileBuffer(dir->chars, &value->deviceClass);
|
||||
ffStrbufSubstrBefore(dir, dirLength);
|
||||
|
||||
return value->name.length > 0 || value->deviceClass.length > 0;
|
||||
}
|
||||
|
||||
const FFTempsResult* ffDetectTemps(const FFinstance* instance)
|
||||
{
|
||||
FF_UNUSED(instance)
|
||||
|
||||
static FFTempsResult result;
|
||||
static pthread_mutex_t mutex = PTHREAD_MUTEX_INITIALIZER;
|
||||
static bool init = false;
|
||||
|
||||
pthread_mutex_lock(&mutex);
|
||||
if(init)
|
||||
{
|
||||
pthread_mutex_unlock(&mutex);
|
||||
return &result;
|
||||
}
|
||||
init = true;
|
||||
|
||||
ffListInitA(&result.values, sizeof(FFTempValue), 16);
|
||||
|
||||
FFstrbuf baseDir;
|
||||
ffStrbufInitA(&baseDir, 64);
|
||||
ffStrbufAppendS(&baseDir, "/sys/class/hwmon/");
|
||||
|
||||
uint32_t baseDirLength = baseDir.length;
|
||||
|
||||
DIR* dirp = opendir(baseDir.chars);
|
||||
if(dirp == NULL)
|
||||
{
|
||||
pthread_mutex_unlock(&mutex);
|
||||
return &result;
|
||||
}
|
||||
|
||||
struct dirent* entry;
|
||||
while((entry = readdir(dirp)) != NULL)
|
||||
{
|
||||
if(entry->d_name[0] == '.')
|
||||
continue;
|
||||
|
||||
ffStrbufAppendS(&baseDir, entry->d_name);
|
||||
ffStrbufAppendC(&baseDir, '/');
|
||||
|
||||
FFTempValue* temp = ffListAdd(&result.values);
|
||||
ffStrbufInit(&temp->name);
|
||||
ffStrbufInit(&temp->deviceClass);
|
||||
if(!parseHwmonDir(&baseDir, temp))
|
||||
{
|
||||
ffStrbufDestroy(&temp->name);
|
||||
ffStrbufDestroy(&temp->deviceClass);
|
||||
--result.values.length;
|
||||
}
|
||||
|
||||
ffStrbufSubstrBefore(&baseDir, baseDirLength);
|
||||
}
|
||||
|
||||
pthread_mutex_unlock(&mutex);
|
||||
return &result;
|
||||
}
|
||||
@@ -1,22 +0,0 @@
|
||||
#pragma once
|
||||
|
||||
#ifndef FF_INCLUDED_detection_temps
|
||||
#define FF_INCLUDED_detection_temps
|
||||
|
||||
#include "fastfetch.h"
|
||||
|
||||
typedef struct FFTempValue
|
||||
{
|
||||
FFstrbuf name;
|
||||
FFstrbuf deviceClass;
|
||||
double value;
|
||||
} FFTempValue;
|
||||
|
||||
typedef struct FFTempsResult
|
||||
{
|
||||
FFlist values; //List of FFTempValue
|
||||
} FFTempsResult;
|
||||
|
||||
const FFTempsResult* ffDetectTemps(const FFinstance* instance);
|
||||
|
||||
#endif
|
||||
@@ -1,282 +0,0 @@
|
||||
#include "fastfetch.h"
|
||||
#include "detection/terminalshell.h"
|
||||
#include "common/io.h"
|
||||
#include "common/parsing.h"
|
||||
#include "common/processing.h"
|
||||
|
||||
#include <ctype.h>
|
||||
#include <string.h>
|
||||
#include <stdlib.h>
|
||||
#include <unistd.h>
|
||||
#include <pthread.h>
|
||||
|
||||
static void setExeName(FFstrbuf* exe, const char** exeName)
|
||||
{
|
||||
uint32_t lastSlashIndex = ffStrbufLastIndexC(exe, '/');
|
||||
if(lastSlashIndex < exe->length)
|
||||
*exeName = exe->chars + lastSlashIndex + 1;
|
||||
}
|
||||
|
||||
static void getProcessInformation(const char* pid, FFstrbuf* processName, FFstrbuf* exe, const char** exeName)
|
||||
{
|
||||
FFstrbuf cmdlineFilePath;
|
||||
ffStrbufInit(&cmdlineFilePath);
|
||||
ffStrbufAppendS(&cmdlineFilePath, "/proc/");
|
||||
ffStrbufAppendS(&cmdlineFilePath, pid);
|
||||
ffStrbufAppendS(&cmdlineFilePath, "/cmdline");
|
||||
|
||||
ffReadFileBuffer(cmdlineFilePath.chars, exe);
|
||||
ffStrbufSubstrBeforeFirstC(exe, '\0'); //Trim the arguments
|
||||
ffStrbufTrimLeft(exe, '-'); //Happens in TTY
|
||||
|
||||
if(exe->length == 0)
|
||||
ffStrbufSet(exe, processName);
|
||||
|
||||
setExeName(exe, exeName);
|
||||
|
||||
ffStrbufDestroy(&cmdlineFilePath);
|
||||
}
|
||||
|
||||
static void getTerminalShell(FFTerminalShellResult* result, const char* pid)
|
||||
{
|
||||
FFstrbuf statFilePath;
|
||||
ffStrbufInit(&statFilePath);
|
||||
ffStrbufAppendS(&statFilePath, "/proc/");
|
||||
ffStrbufAppendS(&statFilePath, pid);
|
||||
ffStrbufAppendS(&statFilePath, "/stat");
|
||||
|
||||
FILE* stat = fopen(statFilePath.chars, "r");
|
||||
|
||||
ffStrbufDestroy(&statFilePath);
|
||||
|
||||
if(stat == NULL)
|
||||
return;
|
||||
|
||||
char name[256];
|
||||
name[0] = '\0';
|
||||
|
||||
char ppid[256];
|
||||
ppid[0] = '\0';
|
||||
|
||||
if(
|
||||
fscanf(stat, "%*s (%255[^)]) %*c %255s", name, ppid) != 2 || //stat (comm) state ppid
|
||||
!ffStrSet(name) ||
|
||||
!ffStrSet(ppid) ||
|
||||
*ppid == '-' ||
|
||||
strcasecmp(ppid, "0") == 0
|
||||
) {
|
||||
fclose(stat);
|
||||
return;
|
||||
}
|
||||
|
||||
fclose(stat);
|
||||
|
||||
//Common programs that are between terminal and own process, but are not the shell
|
||||
if(
|
||||
strcasecmp(name, "sudo") == 0 ||
|
||||
strcasecmp(name, "su") == 0 ||
|
||||
strcasecmp(name, "doas") == 0 ||
|
||||
strcasecmp(name, "strace") == 0 ||
|
||||
strcasecmp(name, "sshd") == 0 ||
|
||||
strcasecmp(name, "gdb") == 0 ||
|
||||
strcasecmp(name, "guake-wrapped") == 0
|
||||
) {
|
||||
getTerminalShell(result, ppid);
|
||||
return;
|
||||
}
|
||||
|
||||
//Known shells
|
||||
if(
|
||||
strcasecmp(name, "bash") == 0 ||
|
||||
strcasecmp(name, "sh") == 0 ||
|
||||
strcasecmp(name, "zsh") == 0 ||
|
||||
strcasecmp(name, "ksh") == 0 ||
|
||||
strcasecmp(name, "fish") == 0 ||
|
||||
strcasecmp(name, "dash") == 0 ||
|
||||
strcasecmp(name, "git-shell") == 0
|
||||
) {
|
||||
ffStrbufAppendS(&result->shellProcessName, name);
|
||||
getProcessInformation(pid, &result->shellProcessName, &result->shellExe, &result->shellExeName);
|
||||
|
||||
getTerminalShell(result, ppid);
|
||||
return;
|
||||
}
|
||||
|
||||
ffStrbufAppendS(&result->terminalProcessName, name);
|
||||
getProcessInformation(pid, &result->terminalProcessName, &result->terminalExe, &result->terminalExeName);
|
||||
}
|
||||
|
||||
static void getTerminalFromEnv(FFTerminalShellResult* result)
|
||||
{
|
||||
if(
|
||||
result->terminalProcessName.length > 0 &&
|
||||
!ffStrbufStartsWithIgnCaseS(&result->terminalProcessName, "login") &&
|
||||
ffStrbufIgnCaseCompS(&result->terminalProcessName, "(login)") != 0 &&
|
||||
ffStrbufIgnCaseCompS(&result->terminalProcessName, "systemd") != 0 &&
|
||||
ffStrbufIgnCaseCompS(&result->terminalProcessName, "init") != 0 &&
|
||||
ffStrbufIgnCaseCompS(&result->terminalProcessName, "(init)") != 0 &&
|
||||
ffStrbufIgnCaseCompS(&result->terminalProcessName, "0") != 0
|
||||
) return;
|
||||
|
||||
char* term = NULL;
|
||||
|
||||
//SSH
|
||||
if(getenv("SSH_CONNECTION") != NULL)
|
||||
term = getenv("SSH_TTY");
|
||||
|
||||
//Windows Terminal
|
||||
if(!ffStrSet(term) && (
|
||||
getenv("WT_SESSION") != NULL ||
|
||||
getenv("WT_PROFILE_ID") != NULL
|
||||
)) term = "Windows Terminal";
|
||||
|
||||
//Termux
|
||||
if(!ffStrSet(term) && (
|
||||
getenv("TERMUX_VERSION") != NULL ||
|
||||
getenv("TERMUX_MAIN_PACKAGE_FORMAT") != NULL ||
|
||||
getenv("TMUX_TMPDIR") != NULL
|
||||
)) term = "Termux";
|
||||
|
||||
//Normal Terminal
|
||||
if(!ffStrSet(term))
|
||||
term = getenv("TERM");
|
||||
|
||||
//TTY
|
||||
if(!ffStrSet(term) || strcasecmp(term, "linux") == 0)
|
||||
term = ttyname(STDIN_FILENO);
|
||||
|
||||
ffStrbufSetS(&result->terminalProcessName, term);
|
||||
ffStrbufSetS(&result->terminalExe, term);
|
||||
setExeName(&result->terminalExe, &result->terminalExeName);
|
||||
}
|
||||
|
||||
static void getUserShellFromEnv(FFTerminalShellResult* result)
|
||||
{
|
||||
ffStrbufAppendS(&result->userShellExe, getenv("SHELL"));
|
||||
setExeName(&result->userShellExe, &result->userShellExeName);
|
||||
|
||||
//If shell detection via processes failed
|
||||
if(result->shellProcessName.length == 0 && result->userShellExe.length > 0)
|
||||
{
|
||||
ffStrbufAppendS(&result->shellProcessName, result->userShellExeName);
|
||||
ffStrbufSet(&result->shellExe, &result->userShellExe);
|
||||
setExeName(&result->shellExe, &result->shellExeName);
|
||||
}
|
||||
}
|
||||
|
||||
static void getShellVersionBash(FFstrbuf* exe, FFstrbuf* version)
|
||||
{
|
||||
ffProcessAppendStdOut(version, (char* const[]) {
|
||||
"env",
|
||||
"-i",
|
||||
exe->chars,
|
||||
"--norc",
|
||||
"--noprofile",
|
||||
"-c",
|
||||
"printf \"%s\" \"$BASH_VERSION\"",
|
||||
NULL
|
||||
});
|
||||
ffStrbufSubstrBeforeFirstC(version, '(');
|
||||
}
|
||||
|
||||
static void getShellVersionZsh(FFstrbuf* exe, FFstrbuf* version)
|
||||
{
|
||||
ffProcessAppendStdOut(version, (char* const[]) {
|
||||
exe->chars,
|
||||
"--version",
|
||||
NULL
|
||||
});
|
||||
ffStrbufTrimRight(version, '\n');
|
||||
ffStrbufSubstrBeforeLastC(version, ' ');
|
||||
ffStrbufSubstrAfterFirstC(version, ' ');
|
||||
}
|
||||
|
||||
static void getShellVersionFish(FFstrbuf* exe, FFstrbuf* version)
|
||||
{
|
||||
ffProcessAppendStdOut(version, (char* const[]) {
|
||||
exe->chars,
|
||||
"--version",
|
||||
NULL
|
||||
});
|
||||
ffStrbufTrimRight(version, '\n');
|
||||
ffStrbufSubstrAfterLastC(version, ' ');
|
||||
}
|
||||
|
||||
static void getShellVersionGeneric(FFstrbuf* exe, const char* exeName, FFstrbuf* version)
|
||||
{
|
||||
FFstrbuf command;
|
||||
ffStrbufInit(&command);
|
||||
ffStrbufAppendS(&command, "printf \"%s\" \"$");
|
||||
ffStrbufAppendTransformS(&command, exeName, toupper);
|
||||
ffStrbufAppendS(&command, "_VERSION\"");
|
||||
|
||||
ffProcessAppendStdOut(version, (char* const[]) {
|
||||
"env",
|
||||
"-i",
|
||||
exe->chars,
|
||||
"-c",
|
||||
command.chars,
|
||||
NULL
|
||||
});
|
||||
ffStrbufSubstrBeforeFirstC(version, '(');
|
||||
ffStrbufRemoveStrings(version, 2, "-release", "release");
|
||||
|
||||
ffStrbufDestroy(&command);
|
||||
}
|
||||
|
||||
static void getShellVersion(FFstrbuf* exe, const char* exeName, FFstrbuf* version)
|
||||
{
|
||||
if(strcasecmp(exeName, "bash") == 0)
|
||||
getShellVersionBash(exe, version);
|
||||
else if(strcasecmp(exeName, "zsh") == 0)
|
||||
getShellVersionZsh(exe, version);
|
||||
else if(strcasecmp(exeName, "fish") == 0)
|
||||
getShellVersionFish(exe, version);
|
||||
else
|
||||
getShellVersionGeneric(exe, exeName, version);
|
||||
}
|
||||
|
||||
const FFTerminalShellResult* ffDetectTerminalShell(FFinstance* instance)
|
||||
{
|
||||
FF_UNUSED(instance);
|
||||
|
||||
static pthread_mutex_t mutex = PTHREAD_MUTEX_INITIALIZER;
|
||||
static FFTerminalShellResult result;
|
||||
static bool init = false;
|
||||
pthread_mutex_lock(&mutex);
|
||||
if(init)
|
||||
{
|
||||
pthread_mutex_unlock(&mutex);
|
||||
return &result;
|
||||
}
|
||||
init = true;
|
||||
|
||||
ffStrbufInit(&result.shellProcessName);
|
||||
ffStrbufInitA(&result.shellExe, 128);
|
||||
result.shellExeName = result.shellExe.chars;
|
||||
ffStrbufInit(&result.shellVersion);
|
||||
|
||||
ffStrbufInit(&result.terminalProcessName);
|
||||
ffStrbufInitA(&result.terminalExe, 128);
|
||||
result.terminalExeName = result.terminalExe.chars;
|
||||
|
||||
ffStrbufInit(&result.userShellExe);
|
||||
result.userShellExeName = result.userShellExe.chars;
|
||||
ffStrbufInit(&result.userShellVersion);
|
||||
|
||||
char ppid[32];
|
||||
snprintf(ppid, sizeof(ppid) - 1, "%i", getppid());
|
||||
getTerminalShell(&result, ppid);
|
||||
|
||||
getTerminalFromEnv(&result);
|
||||
getUserShellFromEnv(&result);
|
||||
getShellVersion(&result.shellExe, result.shellExeName, &result.shellVersion);
|
||||
|
||||
if(strcasecmp(result.shellExeName, result.userShellExeName) != 0)
|
||||
getShellVersion(&result.userShellExe, result.userShellExeName, &result.userShellVersion);
|
||||
else
|
||||
ffStrbufSet(&result.userShellVersion, &result.shellVersion);
|
||||
|
||||
pthread_mutex_unlock(&mutex);
|
||||
return &result;
|
||||
}
|
||||
@@ -1,26 +0,0 @@
|
||||
#pragma once
|
||||
|
||||
#ifndef FF_INCLUDED_detection_terminalshell
|
||||
#define FF_INCLUDED_detection_terminalshell
|
||||
|
||||
#include "fastfetch.h"
|
||||
|
||||
typedef struct FFTerminalShellResult
|
||||
{
|
||||
FFstrbuf shellProcessName;
|
||||
FFstrbuf shellExe;
|
||||
const char* shellExeName; //pointer to a char in shellExe
|
||||
FFstrbuf shellVersion;
|
||||
|
||||
FFstrbuf terminalProcessName;
|
||||
FFstrbuf terminalExe;
|
||||
const char* terminalExeName; //pointer to a char in terminalExe
|
||||
|
||||
FFstrbuf userShellExe;
|
||||
const char* userShellExeName; //pointer to a char in userShellExe
|
||||
FFstrbuf userShellVersion;
|
||||
} FFTerminalShellResult;
|
||||
|
||||
const FFTerminalShellResult* ffDetectTerminalShell(FFinstance* instance);
|
||||
|
||||
#endif
|
||||
@@ -1,38 +0,0 @@
|
||||
#include "fastfetch.h"
|
||||
#include "detection/title.h"
|
||||
|
||||
#include <limits.h>
|
||||
#include <unistd.h>
|
||||
#include <pthread.h>
|
||||
#include <netdb.h>
|
||||
|
||||
const FFTitleResult* ffDetectTitle(const FFinstance* instance)
|
||||
{
|
||||
static FFTitleResult result;
|
||||
|
||||
static pthread_mutex_t mutex = PTHREAD_MUTEX_INITIALIZER;
|
||||
static bool init = false;
|
||||
pthread_mutex_lock(&mutex);
|
||||
if(init)
|
||||
{
|
||||
pthread_mutex_unlock(&mutex);
|
||||
return &result;
|
||||
}
|
||||
init = true;
|
||||
|
||||
ffStrbufInit(&result.userName);
|
||||
ffStrbufAppendS(&result.userName, instance->state.passwd->pw_name);
|
||||
|
||||
ffStrbufInitA(&result.hostname, HOST_NAME_MAX);
|
||||
ffStrbufAppendS(&result.hostname, instance->state.utsname.nodename);
|
||||
|
||||
ffStrbufInitA(&result.fqdn, HOST_NAME_MAX);
|
||||
struct hostent* host = gethostbyname(result.hostname.chars);
|
||||
if(host != NULL)
|
||||
ffStrbufAppendS(&result.fqdn, host->h_name);
|
||||
if(result.fqdn.length == 0)
|
||||
ffStrbufAppend(&result.fqdn, &result.hostname);
|
||||
|
||||
pthread_mutex_unlock(&mutex);
|
||||
return &result;
|
||||
}
|
||||
@@ -1,17 +0,0 @@
|
||||
#pragma once
|
||||
|
||||
#ifndef FF_INCLUDED_detection_title
|
||||
#define FF_INCLUDED_detection_title
|
||||
|
||||
#include "fastfetch.h"
|
||||
|
||||
typedef struct FFTitleResult
|
||||
{
|
||||
FFstrbuf userName;
|
||||
FFstrbuf hostname;
|
||||
FFstrbuf fqdn; //Fully qualified domain name
|
||||
} FFTitleResult;
|
||||
|
||||
const FFTitleResult* ffDetectTitle(const FFinstance* instance);
|
||||
|
||||
#endif
|
||||
@@ -1,231 +0,0 @@
|
||||
#include "fastfetch.h"
|
||||
#include "detection/vulkan.h"
|
||||
|
||||
#include <pthread.h>
|
||||
|
||||
#ifdef FF_HAVE_VULKAN
|
||||
#include "common/library.h"
|
||||
#include "common/io.h"
|
||||
#include "common/parsing.h"
|
||||
#include <stdlib.h>
|
||||
#include <vulkan/vulkan.h>
|
||||
|
||||
static inline void applyVulkanVersion(uint32_t vulkanVersion, FFVersion* ffVersion)
|
||||
{
|
||||
ffVersion->major = VK_VERSION_MAJOR(vulkanVersion);
|
||||
ffVersion->minor = VK_VERSION_MINOR(vulkanVersion);
|
||||
ffVersion->patch = VK_VERSION_PATCH(vulkanVersion);
|
||||
}
|
||||
|
||||
static void applyDriverName(VkPhysicalDeviceDriverProperties* properties, FFstrbuf* result)
|
||||
{
|
||||
if(!ffStrSet(properties->driverName))
|
||||
return;
|
||||
|
||||
ffStrbufAppendS(result, properties->driverName);
|
||||
|
||||
if(!ffStrSet(properties->driverInfo))
|
||||
return;
|
||||
|
||||
ffStrbufAppendS(result, " [");
|
||||
ffStrbufAppendS(result, properties->driverInfo);
|
||||
ffStrbufAppendC(result, ']');
|
||||
}
|
||||
|
||||
static void detectVulkan(const FFinstance* instance, FFVulkanResult* result)
|
||||
{
|
||||
FF_LIBRARY_LOAD(vulkan, instance->config.libVulkan, , "libvulkan.so", 2)
|
||||
FF_LIBRARY_LOAD_SYMBOL(vulkan, vkGetInstanceProcAddr,)
|
||||
FF_LIBRARY_LOAD_SYMBOL(vulkan, vkCreateInstance,)
|
||||
FF_LIBRARY_LOAD_SYMBOL(vulkan, vkDestroyInstance,)
|
||||
FF_LIBRARY_LOAD_SYMBOL(vulkan, vkEnumeratePhysicalDevices,)
|
||||
FF_LIBRARY_LOAD_SYMBOL(vulkan, vkGetPhysicalDeviceProperties,)
|
||||
|
||||
//Some drivers (nvdc) print messages to stdout
|
||||
//and thats the best way i found to disable that
|
||||
ffSuppressIO(true);
|
||||
|
||||
FFVersion instanceVersion = FF_VERSION_INIT;
|
||||
|
||||
//We need to get the function pointer this way, because it is only provided by vulkan 1.1 and higher.
|
||||
//a dlsym would fail on 1.0 implementations
|
||||
PFN_vkEnumerateInstanceVersion ffvkEnumerateInstanceVersion = (PFN_vkEnumerateInstanceVersion) ffvkGetInstanceProcAddr(NULL, "vkEnumerateInstanceVersion");
|
||||
if(ffvkEnumerateInstanceVersion != NULL)
|
||||
{
|
||||
uint32_t version;
|
||||
if(ffvkEnumerateInstanceVersion(&version) == VK_SUCCESS)
|
||||
applyVulkanVersion(version, &instanceVersion);
|
||||
}
|
||||
|
||||
const uint32_t projectVersion = VK_MAKE_VERSION(
|
||||
FASTFETCH_PROJECT_VERSION_MAJOR,
|
||||
FASTFETCH_PROJECT_VERSION_MINOR,
|
||||
FASTFETCH_PROJECT_VERSION_PATCH
|
||||
);
|
||||
|
||||
//We need to request 1.2 to get physicalDeviceDriverProperties
|
||||
uint32_t requestedVkVersion = VK_API_VERSION_1_0;
|
||||
if(instanceVersion.minor >= 2)
|
||||
requestedVkVersion = VK_API_VERSION_1_2;
|
||||
|
||||
const VkApplicationInfo applicationInfo = {
|
||||
.sType = VK_STRUCTURE_TYPE_APPLICATION_INFO,
|
||||
.pNext = NULL,
|
||||
.pApplicationName = FASTFETCH_PROJECT_NAME,
|
||||
.applicationVersion = projectVersion,
|
||||
.pEngineName = "vulkanPrintGPUs",
|
||||
.engineVersion = projectVersion,
|
||||
.apiVersion = requestedVkVersion
|
||||
};
|
||||
|
||||
const VkInstanceCreateInfo instanceCreateInfo = {
|
||||
.sType = VK_STRUCTURE_TYPE_INSTANCE_CREATE_INFO,
|
||||
.pNext = NULL,
|
||||
.pApplicationInfo = &applicationInfo,
|
||||
.enabledLayerCount = 0,
|
||||
.ppEnabledLayerNames = NULL,
|
||||
.enabledExtensionCount = 0,
|
||||
.ppEnabledExtensionNames = NULL,
|
||||
.flags = 0
|
||||
};
|
||||
|
||||
VkInstance vkInstance;
|
||||
if(ffvkCreateInstance(&instanceCreateInfo, NULL, &vkInstance) != VK_SUCCESS)
|
||||
{
|
||||
dlclose(vulkan);
|
||||
ffSuppressIO(false);
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
//if instance creation succeeded, but vkEnumerateInstanceVersion didn't, this means we are running against a vulkan 1.0 implementation
|
||||
//explicitly set this version, if no device is found, so we still have at least this info
|
||||
if(instanceVersion.major == 0 && instanceVersion.minor == 0 && instanceVersion.patch == 0)
|
||||
instanceVersion.major = 1;
|
||||
|
||||
uint32_t physicalDeviceCount;
|
||||
if(ffvkEnumeratePhysicalDevices(vkInstance, &physicalDeviceCount, NULL) != VK_SUCCESS)
|
||||
{
|
||||
ffvkDestroyInstance(vkInstance, NULL);
|
||||
dlclose(vulkan);
|
||||
ffSuppressIO(false);
|
||||
return;
|
||||
}
|
||||
|
||||
VkPhysicalDevice* physicalDevices = malloc(sizeof(VkPhysicalDevice) * physicalDeviceCount);
|
||||
if(ffvkEnumeratePhysicalDevices(vkInstance, &physicalDeviceCount, physicalDevices) != VK_SUCCESS)
|
||||
{
|
||||
free(physicalDevices);
|
||||
ffvkDestroyInstance(vkInstance, NULL);
|
||||
dlclose(vulkan);
|
||||
ffSuppressIO(false);
|
||||
return;
|
||||
}
|
||||
|
||||
PFN_vkGetPhysicalDeviceProperties2 ffvkGetPhysicalDeviceProperties2 = (PFN_vkGetPhysicalDeviceProperties2) ffvkGetInstanceProcAddr(vkInstance, "vkGetPhysicalDeviceProperties2");
|
||||
|
||||
FFVersion maxDeviceApiVersion = FF_VERSION_INIT;
|
||||
FFVersion maxDeviceConformanceVersion = FF_VERSION_INIT;
|
||||
|
||||
for(uint32_t i = 0; i < physicalDeviceCount; i++)
|
||||
{
|
||||
//Get device properties.
|
||||
//On VK 1.2 and up, we use vkGetPhysicalDeviceProperties2, so we can put VkPhysicalDeviceDriverProperties in the pNext chain.
|
||||
//This is required to get the driver name and conformance version.
|
||||
|
||||
VkPhysicalDeviceDriverProperties driverProperties;
|
||||
driverProperties.sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_DRIVER_PROPERTIES;
|
||||
driverProperties.pNext = NULL;
|
||||
driverProperties.driverName[0] = '\0';
|
||||
driverProperties.conformanceVersion = (VkConformanceVersion) {0, 0, 0, 0};
|
||||
|
||||
VkPhysicalDeviceProperties2 physicalDeviceProperties;
|
||||
physicalDeviceProperties.sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_PROPERTIES_2;
|
||||
physicalDeviceProperties.pNext = &driverProperties;
|
||||
|
||||
if(ffvkGetPhysicalDeviceProperties2 != NULL)
|
||||
ffvkGetPhysicalDeviceProperties2(physicalDevices[i], &physicalDeviceProperties);
|
||||
else
|
||||
ffvkGetPhysicalDeviceProperties(physicalDevices[i], &physicalDeviceProperties.properties);
|
||||
|
||||
//If the device api version is higher than the current highest device api version, overwrite it
|
||||
//In this case, also use the current device driver name as the shown driver name
|
||||
|
||||
FFVersion deviceAPIVersion = FF_VERSION_INIT;
|
||||
applyVulkanVersion(physicalDeviceProperties.properties.apiVersion, &deviceAPIVersion);
|
||||
if(ffVersionCompare(&deviceAPIVersion, &maxDeviceApiVersion) > 0)
|
||||
{
|
||||
maxDeviceApiVersion = deviceAPIVersion;
|
||||
applyDriverName(&driverProperties, &result->driver);
|
||||
}
|
||||
|
||||
//If the device conformance version is higher than the current highest device conformance version, overwrite it
|
||||
|
||||
FFVersion deviceConformanceVersion = FF_VERSION_INIT;
|
||||
deviceConformanceVersion.major = driverProperties.conformanceVersion.major;
|
||||
deviceConformanceVersion.minor = driverProperties.conformanceVersion.minor;
|
||||
deviceConformanceVersion.patch = driverProperties.conformanceVersion.patch;
|
||||
|
||||
if(ffVersionCompare(&deviceConformanceVersion, &maxDeviceConformanceVersion) > 0)
|
||||
maxDeviceConformanceVersion = deviceConformanceVersion;
|
||||
|
||||
//Add the device to the list of devices shown by the GPU module
|
||||
|
||||
//We don't want softare rasterizers to show up as physical gpu
|
||||
if(physicalDeviceProperties.properties.deviceType == VK_PHYSICAL_DEVICE_TYPE_CPU)
|
||||
continue;
|
||||
|
||||
FFGPUResult* gpu = ffListAdd(&result->devices);
|
||||
ffStrbufInit(&gpu->vendor);
|
||||
ffStrbufInit(&gpu->name);
|
||||
ffStrbufInit(&gpu->driver);
|
||||
gpu->temperature = 0.0 / 0.0; //NaN, no way to detect temperature using vulkan
|
||||
ffStrbufAppendS(&gpu->name, physicalDeviceProperties.properties.deviceName);
|
||||
}
|
||||
|
||||
//If the highest device version is lower than the instance version, use it as our vulkan version
|
||||
//otherwise the instance version is the vulkan version
|
||||
if(ffVersionCompare(&instanceVersion, &maxDeviceApiVersion) > 0)
|
||||
ffVersionToPretty(&maxDeviceApiVersion, &result->apiVersion);
|
||||
else
|
||||
ffVersionToPretty(&instanceVersion, &result->apiVersion);
|
||||
|
||||
//Use the highest device conformace version as our conformance version
|
||||
ffVersionToPretty(&maxDeviceConformanceVersion, &result->conformanceVersion);
|
||||
|
||||
free(physicalDevices);
|
||||
ffvkDestroyInstance(vkInstance, NULL);
|
||||
dlclose(vulkan);
|
||||
ffSuppressIO(false);
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
const FFVulkanResult* ffDetectVulkan(const FFinstance* instance)
|
||||
{
|
||||
static FFVulkanResult result;
|
||||
static pthread_mutex_t mutex = PTHREAD_MUTEX_INITIALIZER;
|
||||
static bool init = false;
|
||||
|
||||
pthread_mutex_lock(&mutex);
|
||||
if(init)
|
||||
{
|
||||
pthread_mutex_unlock(&mutex);
|
||||
return &result;
|
||||
}
|
||||
init = true;
|
||||
|
||||
ffStrbufInit(&result.driver);
|
||||
ffStrbufInit(&result.apiVersion);
|
||||
ffStrbufInit(&result.conformanceVersion);
|
||||
ffListInit(&result.devices, sizeof(FFGPUResult));
|
||||
|
||||
#ifdef FF_HAVE_VULKAN
|
||||
detectVulkan(instance, &result);
|
||||
#else
|
||||
FF_UNUSED(instance);
|
||||
#endif
|
||||
|
||||
pthread_mutex_unlock(&mutex);
|
||||
return &result;
|
||||
}
|
||||
@@ -1,26 +0,0 @@
|
||||
#pragma once
|
||||
|
||||
#ifndef FF_INCLUDED_detection_vulkan
|
||||
#define FF_INCLUDED_detection_vulkan
|
||||
|
||||
#include "fastfetch.h"
|
||||
|
||||
typedef struct FFGPUResult
|
||||
{
|
||||
FFstrbuf vendor;
|
||||
FFstrbuf name;
|
||||
FFstrbuf driver;
|
||||
double temperature;
|
||||
} FFGPUResult;
|
||||
|
||||
typedef struct FFVulkanResult
|
||||
{
|
||||
FFstrbuf driver;
|
||||
FFstrbuf apiVersion;
|
||||
FFstrbuf conformanceVersion;
|
||||
FFlist devices; //List of FFGPUResult
|
||||
} FFVulkanResult;
|
||||
|
||||
const FFVulkanResult* ffDetectVulkan(const FFinstance* instance);
|
||||
|
||||
#endif
|
||||
-1403
File diff suppressed because it is too large
Load Diff
-249
@@ -1,249 +0,0 @@
|
||||
#pragma once
|
||||
|
||||
#ifndef FASTFETCH_INCLUDED
|
||||
#define FASTFETCH_INCLUDED
|
||||
|
||||
#include "fastfetch_config.h"
|
||||
|
||||
#include <stdint.h>
|
||||
#include <stdbool.h>
|
||||
|
||||
#include <pwd.h>
|
||||
#include <sys/utsname.h>
|
||||
#include <sys/sysinfo.h>
|
||||
|
||||
#include "util/FFstrbuf.h"
|
||||
#include "util/FFlist.h"
|
||||
|
||||
static inline void ffUnused(int dummy, ...) { (void) dummy; }
|
||||
#define FF_UNUSED(...) ffUnused(0, __VA_ARGS__);
|
||||
|
||||
#define FASTFETCH_TEXT_MODIFIER_BOLT "\033[1m"
|
||||
#define FASTFETCH_TEXT_MODIFIER_ERROR "\033[1;31m"
|
||||
#define FASTFETCH_TEXT_MODIFIER_RESET "\033[0m"
|
||||
|
||||
#define FASTFETCH_LOGO_MAX_COLORS 9 //two digits would make parsing much more complicated (index 1 - 9)
|
||||
|
||||
typedef enum FFLogoType
|
||||
{
|
||||
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 enum FFGLType
|
||||
{
|
||||
FF_GL_TYPE_AUTO,
|
||||
FF_GL_TYPE_EGL,
|
||||
FF_GL_TYPE_GLX,
|
||||
FF_GL_TYPE_OSMESA
|
||||
} FFGLType;
|
||||
|
||||
typedef struct FFModuleArgs
|
||||
{
|
||||
FFstrbuf key;
|
||||
FFstrbuf outputFormat;
|
||||
FFstrbuf errorFormat;
|
||||
} FFModuleArgs;
|
||||
|
||||
typedef struct FFconfig
|
||||
{
|
||||
struct
|
||||
{
|
||||
FFstrbuf source;
|
||||
FFLogoType type;
|
||||
FFstrbuf colors[FASTFETCH_LOGO_MAX_COLORS];
|
||||
uint32_t width;
|
||||
uint32_t height;
|
||||
uint32_t paddingLeft;
|
||||
uint32_t paddingRight;
|
||||
bool printRemaining;
|
||||
} logo;
|
||||
|
||||
FFstrbuf mainColor; //If this is empty, ffLogoPrint will set it to the main color of the logo
|
||||
FFstrbuf separator;
|
||||
|
||||
bool showErrors;
|
||||
bool recache;
|
||||
bool cacheSave;
|
||||
bool allowSlowOperations;
|
||||
bool disableLinewrap;
|
||||
bool hideCursor;
|
||||
bool escapeBedrock;
|
||||
FFGLType glType;
|
||||
bool pipe; //disables logo and all escape sequences
|
||||
bool multithreading;
|
||||
|
||||
FFModuleArgs os;
|
||||
FFModuleArgs host;
|
||||
FFModuleArgs kernel;
|
||||
FFModuleArgs uptime;
|
||||
FFModuleArgs processes;
|
||||
FFModuleArgs packages;
|
||||
FFModuleArgs shell;
|
||||
FFModuleArgs resolution;
|
||||
FFModuleArgs de;
|
||||
FFModuleArgs wm;
|
||||
FFModuleArgs wmTheme;
|
||||
FFModuleArgs theme;
|
||||
FFModuleArgs icons;
|
||||
FFModuleArgs font;
|
||||
FFModuleArgs cursor;
|
||||
FFModuleArgs terminal;
|
||||
FFModuleArgs terminalFont;
|
||||
FFModuleArgs cpu;
|
||||
FFModuleArgs cpuUsage;
|
||||
FFModuleArgs gpu;
|
||||
FFModuleArgs memory;
|
||||
FFModuleArgs disk;
|
||||
FFModuleArgs battery;
|
||||
FFModuleArgs locale;
|
||||
FFModuleArgs localIP;
|
||||
FFModuleArgs publicIP;
|
||||
FFModuleArgs player;
|
||||
FFModuleArgs song;
|
||||
FFModuleArgs dateTime;
|
||||
FFModuleArgs date;
|
||||
FFModuleArgs time;
|
||||
FFModuleArgs vulkan;
|
||||
FFModuleArgs openGL;
|
||||
FFModuleArgs openCL;
|
||||
|
||||
FFstrbuf libPCI;
|
||||
FFstrbuf libVulkan;
|
||||
FFstrbuf libWayland;
|
||||
FFstrbuf libXcbRandr;
|
||||
FFstrbuf libXcb;
|
||||
FFstrbuf libXrandr;
|
||||
FFstrbuf libX11;
|
||||
FFstrbuf libGIO;
|
||||
FFstrbuf libDConf;
|
||||
FFstrbuf libDBus;
|
||||
FFstrbuf libXFConf;
|
||||
FFstrbuf libSQLite3;
|
||||
FFstrbuf librpm;
|
||||
FFstrbuf libImageMagick;
|
||||
FFstrbuf libZ;
|
||||
FFstrbuf libChafa;
|
||||
FFstrbuf libEGL;
|
||||
FFstrbuf libGLX;
|
||||
FFstrbuf libOSMesa;
|
||||
FFstrbuf libOpenCL;
|
||||
|
||||
bool titleFQDN;
|
||||
|
||||
FFstrbuf diskFolders;
|
||||
|
||||
FFstrbuf batteryDir;
|
||||
|
||||
FFstrbuf separatorString;
|
||||
|
||||
bool localIpShowLoop;
|
||||
bool localIpShowIpV4;
|
||||
bool localIpShowIpV6;
|
||||
|
||||
uint32_t publicIpTimeout;
|
||||
|
||||
FFstrbuf osFile;
|
||||
|
||||
FFstrbuf playerName;
|
||||
} FFconfig;
|
||||
|
||||
typedef struct FFstate
|
||||
{
|
||||
uint32_t logoWidth;
|
||||
uint32_t logoHeight;
|
||||
uint32_t keysHeight;
|
||||
|
||||
struct passwd* passwd;
|
||||
struct utsname utsname;
|
||||
struct sysinfo sysinfo;
|
||||
|
||||
FFlist configDirs;
|
||||
FFstrbuf cacheDir;
|
||||
} FFstate;
|
||||
|
||||
typedef struct FFinstance
|
||||
{
|
||||
FFconfig config;
|
||||
FFstate state;
|
||||
} FFinstance;
|
||||
|
||||
//////////////////////
|
||||
// Init functions //
|
||||
//////////////////////
|
||||
|
||||
//common/init.c
|
||||
void ffInitInstance(FFinstance* instance);
|
||||
void ffStart(FFinstance* instance);
|
||||
void ffFinish(FFinstance* instance);
|
||||
|
||||
void ffListFeatures();
|
||||
|
||||
////////////////////
|
||||
// Logo functions //
|
||||
////////////////////
|
||||
|
||||
void ffLogoPrint(FFinstance* instance);
|
||||
void ffLogoPrintRemaining(FFinstance* instance);
|
||||
void ffLogoPrintLine(FFinstance* instance);
|
||||
|
||||
void ffLogoBuiltinPrint(FFinstance* instance);
|
||||
void ffLogoBuiltinList();
|
||||
void ffLogoBuiltinListAutocompletion();
|
||||
|
||||
//////////////////////
|
||||
// Module functions //
|
||||
//////////////////////
|
||||
|
||||
//Common
|
||||
|
||||
void ffPrintDateTimeFormat(FFinstance* instance, const char* moduleName, const FFModuleArgs* moduleArgs);
|
||||
|
||||
//Printing
|
||||
|
||||
void ffPrintCustom(FFinstance* instance, const char* key, const char* value);
|
||||
void ffPrintBreak(FFinstance* instance);
|
||||
void ffPrintTitle(FFinstance* instance);
|
||||
void ffPrintSeparator(FFinstance* instance);
|
||||
void ffPrintOS(FFinstance* instance);
|
||||
void ffPrintHost(FFinstance* instance);
|
||||
void ffPrintKernel(FFinstance* instance);
|
||||
void ffPrintUptime(FFinstance* instance);
|
||||
void ffPrintProcesses(FFinstance* instance);
|
||||
void ffPrintPackages(FFinstance* instance);
|
||||
void ffPrintShell(FFinstance* instance);
|
||||
void ffPrintResolution(FFinstance* instance);
|
||||
void ffPrintDesktopEnvironment(FFinstance* instance);
|
||||
void ffPrintWM(FFinstance* instance);
|
||||
void ffPrintWMTheme(FFinstance* instance);
|
||||
void ffPrintTheme(FFinstance* instance);
|
||||
void ffPrintIcons(FFinstance* instance);
|
||||
void ffPrintFont(FFinstance* instance);
|
||||
void ffPrintCursor(FFinstance* instance);
|
||||
void ffPrintTerminal(FFinstance* instance);
|
||||
void ffPrintTerminalFont(FFinstance* instance);
|
||||
void ffPrintCPU(FFinstance* instance);
|
||||
void ffPrintCPUUsage(FFinstance* instance);
|
||||
void ffPrintGPU(FFinstance* instance);
|
||||
void ffPrintMemory(FFinstance* instance);
|
||||
void ffPrintDisk(FFinstance* instance);
|
||||
void ffPrintBattery(FFinstance* instance);
|
||||
void ffPrintLocale(FFinstance* instance);
|
||||
void ffPrintPlayer(FFinstance* instance);
|
||||
void ffPrintSong(FFinstance* instance);
|
||||
void ffPrintDateTime(FFinstance* instance);
|
||||
void ffPrintDate(FFinstance* instance);
|
||||
void ffPrintTime(FFinstance* instance);
|
||||
void ffPrintLocalIp(FFinstance* instance);
|
||||
void ffPrintPublicIp(FFinstance* instance);
|
||||
void ffPrintColors(FFinstance* instance);
|
||||
void ffPrintVulkan(FFinstance* instance);
|
||||
void ffPrintOpenGL(FFinstance* instance);
|
||||
void ffPrintOpenCL(FFinstance* instance);
|
||||
|
||||
#endif
|
||||
@@ -1,24 +0,0 @@
|
||||
#ifndef FASTFETCH_INDLUDED_fastfetch_config_h_in
|
||||
#define FASTFETCH_INDLUDED_fastfetch_config_h_in
|
||||
|
||||
#define FASTFETCH_PROJECT_NAME "@PROJECT_NAME@"
|
||||
#define FASTFETCH_PROJECT_VERSION "@PROJECT_VERSION@"
|
||||
#define FASTFETCH_PROJECT_VERSION_MAJOR @PROJECT_VERSION_MAJOR@
|
||||
#define FASTFETCH_PROJECT_VERSION_MINOR @PROJECT_VERSION_MINOR@
|
||||
#define FASTFETCH_PROJECT_VERSION_PATCH @PROJECT_VERSION_PATCH@
|
||||
#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_SYSTEM "@DATATEXT_CONFIG_SYSTEM@" //Requires FASTFETCH_PROJECT_VERSION to be set
|
||||
#define FASTFETCH_DATATEXT_CONFIG_USER "@DATATEXT_CONFIG_USER@" //Requires FASTFETCH_PROJECT_VERSION and FASTFETCH_DATATEXT_STRUCTURE to be set
|
||||
#define FASTFETCH_DATATEXT_MODULES "@DATATEXT_MODULES@"
|
||||
#define FASTFETCH_DATATEXT_HELP "@DATATEXT_HELP@"
|
||||
#define FASTFETCH_DATATEXT_HELP_COLOR "@DATATEXT_HELP_COLOR@"
|
||||
#define FASTFETCH_DATATEXT_HELP_FORMAT "@DATATEXT_HELP_FORMAT@"
|
||||
#define FASTFETCH_DATATEXT_HELP_CONFIG "@DATATEXT_HELP_CONFIG@"
|
||||
|
||||
#endif // FASTFETCH_INDLUDED_fastfetch_config_h_in
|
||||
@@ -1,57 +0,0 @@
|
||||
#include "fastfetch.h"
|
||||
|
||||
int main(int argc, char** argv)
|
||||
{
|
||||
//Disable compiler warnings
|
||||
FF_UNUSED(argc, argv);
|
||||
|
||||
FFinstance instance;
|
||||
ffInitInstance(&instance); //This also applys default configuration to instance.config
|
||||
|
||||
//Modify instance.config here
|
||||
|
||||
//Does things like starting detection threads, disabling line wrap, etc
|
||||
ffStart(&instance);
|
||||
|
||||
//Printing
|
||||
ffPrintTitle(&instance);
|
||||
ffPrintSeparator(&instance);
|
||||
ffPrintOS(&instance);
|
||||
ffPrintHost(&instance);
|
||||
ffPrintKernel(&instance);
|
||||
ffPrintUptime(&instance);
|
||||
//ffPrintProcesses(&instance);
|
||||
ffPrintPackages(&instance);
|
||||
ffPrintShell(&instance);
|
||||
ffPrintResolution(&instance);
|
||||
ffPrintDesktopEnvironment(&instance);
|
||||
ffPrintWM(&instance);
|
||||
ffPrintWMTheme(&instance);
|
||||
ffPrintTheme(&instance);
|
||||
ffPrintIcons(&instance);
|
||||
ffPrintFont(&instance);
|
||||
ffPrintCursor(&instance);
|
||||
ffPrintTerminal(&instance);
|
||||
ffPrintTerminalFont(&instance);
|
||||
ffPrintCPU(&instance);
|
||||
//ffPrintCPUUsage(&instance);
|
||||
ffPrintGPU(&instance);
|
||||
ffPrintMemory(&instance);
|
||||
ffPrintDisk(&instance);
|
||||
ffPrintBattery(&instance);
|
||||
//ffPrintPlayer(&instance);
|
||||
//ffPrintSong(&instance);
|
||||
//ffPrintLocalIp(&instance);
|
||||
//ffPrintPublicIp(&instance);
|
||||
ffPrintLocale(&instance);
|
||||
//ffPrintDateTime(&instance);
|
||||
//ffPrintDate(&instance);
|
||||
//ffPrintTime(&instance);
|
||||
//ffPrintVulkan(&instance);
|
||||
//ffPrintOpenGL(&instance);
|
||||
ffPrintBreak(&instance);
|
||||
ffPrintColors(&instance);
|
||||
|
||||
ffFinish(&instance);
|
||||
return 0;
|
||||
}
|
||||
-1553
File diff suppressed because it is too large
Load Diff
@@ -1,31 +0,0 @@
|
||||
#ifdef FF_HAVE_IMAGEMAGICK6
|
||||
|
||||
#include "image.h"
|
||||
#include "common/library.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
|
||||
@@ -1,31 +0,0 @@
|
||||
#ifdef FF_HAVE_IMAGEMAGICK7
|
||||
|
||||
#include "image.h"
|
||||
#include "common/library.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
|
||||
@@ -1,574 +0,0 @@
|
||||
#include "image.h"
|
||||
#include "common/io.h"
|
||||
#include "common/printing.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 "common/library.h"
|
||||
#include <stdlib.h>
|
||||
#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);
|
||||
ffWriteFileBuffer(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.logo.paddingLeft + instance->config.logo.paddingRight;
|
||||
|
||||
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.logo.width == 0)
|
||||
writeCacheUint32(requestData, instance->state.logoWidth, FF_CACHE_FILE_WIDTH);
|
||||
|
||||
if(instance->config.logo.height == 0)
|
||||
writeCacheUint32(requestData, instance->state.logoHeight, FF_CACHE_FILE_HEIGHT);
|
||||
|
||||
//Write result to stdout
|
||||
ffPrintCharTimes(' ', instance->config.logo.paddingLeft);
|
||||
fflush(stdout);
|
||||
ffWriteFDBuffer(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;
|
||||
|
||||
ffLogoPrintChars(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.logo.source.chars, instance->config.logo.source.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);
|
||||
ffAppendFileBuffer(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;
|
||||
}
|
||||
|
||||
ffLogoPrintChars(instance, content.chars, false);
|
||||
ffStrbufDestroy(&content);
|
||||
return true;
|
||||
}
|
||||
|
||||
static bool printCachedPixel(FFinstance* instance, FFLogoRequestData* requestData)
|
||||
{
|
||||
requestData->logoCharacterWidth = instance->config.logo.width;
|
||||
if(requestData->logoCharacterWidth == 0)
|
||||
{
|
||||
requestData->logoCharacterWidth = readCachedUint32(requestData, FF_CACHE_FILE_WIDTH);
|
||||
if(requestData->logoCharacterWidth == 0)
|
||||
return false;
|
||||
}
|
||||
|
||||
requestData->logoCharacterHeight = instance->config.logo.height;
|
||||
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.logo.paddingLeft);
|
||||
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.logo.paddingLeft + instance->config.logo.paddingRight;
|
||||
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;
|
||||
}
|
||||
|
||||
bool ffLogoPrintImageIfExists(FFinstance* instance, FFLogoType type)
|
||||
{
|
||||
//Performance optimisation
|
||||
#ifndef FF_HAVE_CHAFA
|
||||
if(type == FF_LOGO_TYPE_CHAFA)
|
||||
return false;
|
||||
#endif
|
||||
|
||||
FFLogoRequestData requestData;
|
||||
requestData.type = type;
|
||||
requestData.characterPixelWidth = 1;
|
||||
requestData.characterPixelHeight = 1;
|
||||
|
||||
if(
|
||||
(type != FF_LOGO_TYPE_CHAFA || instance->config.logo.width == 0 || instance->config.logo.height == 0) &&
|
||||
!getCharacterPixelDimensions(&requestData)
|
||||
)
|
||||
return false;
|
||||
|
||||
requestData.logoPixelWidth = simpleCeil((double) instance->config.logo.width * requestData.characterPixelWidth);
|
||||
requestData.logoPixelHeight = simpleCeil((double) instance->config.logo.height * 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.logo.source.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 false;
|
||||
}
|
||||
ffStrbufRecalculateLength(&requestData.cacheDir);
|
||||
ffStrbufEnsureEndsWithC(&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 true;
|
||||
}
|
||||
|
||||
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 == FF_LOGO_IMAGE_RESULT_SUCCESS;
|
||||
}
|
||||
|
||||
#else //FF_HAVE_IMAGEMAGICK{6, 7}
|
||||
bool ffLogoPrintImageIfExists(FFinstance* instance, FFLogoType type)
|
||||
{
|
||||
FF_UNUSED(instance, type);
|
||||
return false;
|
||||
}
|
||||
#endif //FF_HAVE_IMAGEMAGICK{6, 7}
|
||||
@@ -1,54 +0,0 @@
|
||||
#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 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;
|
||||
|
||||
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
|
||||
-417
@@ -1,417 +0,0 @@
|
||||
#include "logo.h"
|
||||
#include "common/io.h"
|
||||
#include "common/printing.h"
|
||||
#include "detection/os.h"
|
||||
#include "detection/terminalshell.h"
|
||||
|
||||
#include <ctype.h>
|
||||
#include <string.h>
|
||||
|
||||
void ffLogoPrintChars(FFinstance* instance, const char* data, bool doColorReplacement)
|
||||
{
|
||||
uint32_t currentlineLength = 0;
|
||||
|
||||
fputs(FASTFETCH_TEXT_MODIFIER_BOLT, stdout);
|
||||
ffPrintCharTimes(' ', instance->config.logo.paddingLeft);
|
||||
|
||||
//Use logoColor[0] as the default color
|
||||
if(doColorReplacement)
|
||||
ffPrintColor(&instance->config.logo.colors[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.logo.paddingRight);
|
||||
|
||||
//We have \r\n, skip the \r
|
||||
if(*data == '\r')
|
||||
++data;
|
||||
|
||||
putchar('\n');
|
||||
++data;
|
||||
|
||||
ffPrintCharTimes(' ', instance->config.logo.paddingLeft);
|
||||
|
||||
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.logo.colors[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.logo.paddingRight);
|
||||
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.logo.paddingLeft + instance->config.logo.paddingRight;
|
||||
|
||||
//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 logoApplyMainColor(FFinstance* instance, const FFlogo* logo)
|
||||
{
|
||||
if(instance->config.mainColor.length == 0)
|
||||
ffStrbufAppendS(&instance->config.mainColor, logo->builtinColors[0]);
|
||||
}
|
||||
|
||||
static bool logoHasName(const FFlogo* logo, const char* name)
|
||||
{
|
||||
const char** logoName = logo->names;
|
||||
|
||||
while(*logoName != NULL)
|
||||
{
|
||||
if(strcasecmp(*logoName, name) == 0)
|
||||
return true;
|
||||
++logoName;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
static const FFlogo* logoGetBuiltin(const char* name)
|
||||
{
|
||||
GetLogoMethod* methods = ffLogoBuiltinGetAll();
|
||||
|
||||
while(*methods != NULL)
|
||||
{
|
||||
const FFlogo* logo = (*methods)();
|
||||
|
||||
if(logoHasName(logo, name))
|
||||
return logo;
|
||||
|
||||
++methods;
|
||||
}
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
static const FFlogo* logoGetBuiltinDetected(FFinstance* instance)
|
||||
{
|
||||
const FFOSResult* os = ffDetectOS(instance);
|
||||
|
||||
const FFlogo* logo = logoGetBuiltin(os->id.chars);
|
||||
if(logo != NULL)
|
||||
return logo;
|
||||
|
||||
logo = logoGetBuiltin(os->name.chars);
|
||||
if(logo != NULL)
|
||||
return logo;
|
||||
|
||||
logo = logoGetBuiltin(os->prettyName.chars);
|
||||
if(logo != NULL)
|
||||
return logo;
|
||||
|
||||
logo = logoGetBuiltin(os->idLike.chars);
|
||||
if(logo != NULL)
|
||||
return logo;
|
||||
|
||||
logo = logoGetBuiltin(os->systemName.chars);
|
||||
if(logo != NULL)
|
||||
return logo;
|
||||
|
||||
return ffLogoBuiltinGetUnknown();
|
||||
}
|
||||
|
||||
static inline void logoApplyMainColorDetected(FFinstance* instance)
|
||||
{
|
||||
logoApplyMainColor(instance, logoGetBuiltinDetected(instance));
|
||||
}
|
||||
|
||||
static void logoPrintStruct(FFinstance* instance, const FFlogo* logo)
|
||||
{
|
||||
logoApplyMainColor(instance, logo);
|
||||
|
||||
const char** colors = logo->builtinColors;
|
||||
for(int i = 0; *colors != NULL && i < FASTFETCH_LOGO_MAX_COLORS; i++, colors++)
|
||||
{
|
||||
if(instance->config.logo.colors[i].length == 0)
|
||||
ffStrbufAppendS(&instance->config.logo.colors[i], *colors);
|
||||
}
|
||||
|
||||
ffLogoPrintChars(instance, logo->data, true);
|
||||
}
|
||||
|
||||
static bool logoPrintBuiltinIfExists(FFinstance* instance, const char* name)
|
||||
{
|
||||
const FFlogo* logo = logoGetBuiltin(name);
|
||||
if(logo == NULL)
|
||||
return false;
|
||||
|
||||
logoPrintStruct(instance, logo);
|
||||
return true;
|
||||
}
|
||||
|
||||
static inline void logoPrintDetected(FFinstance* instance)
|
||||
{
|
||||
logoPrintStruct(instance, logoGetBuiltinDetected(instance));
|
||||
}
|
||||
|
||||
static bool logoPrintFileIfExists(FFinstance* instance, bool doColorReplacement)
|
||||
{
|
||||
FFstrbuf content;
|
||||
ffStrbufInitA(&content, 2047);
|
||||
|
||||
if(!ffAppendFileBuffer(instance->config.logo.source.chars, &content))
|
||||
{
|
||||
ffStrbufDestroy(&content);
|
||||
return false;
|
||||
}
|
||||
|
||||
logoApplyMainColorDetected(instance);
|
||||
ffLogoPrintChars(instance, content.chars, doColorReplacement);
|
||||
ffStrbufDestroy(&content);
|
||||
return true;
|
||||
}
|
||||
|
||||
static bool logoPrintImageIfExists(FFinstance* instance, FFLogoType logo)
|
||||
{
|
||||
if(!ffLogoPrintImageIfExists(instance, logo))
|
||||
return false;
|
||||
|
||||
logoApplyMainColorDetected(instance);
|
||||
return true;
|
||||
}
|
||||
|
||||
static void logoPrintKnownType(FFinstance* instance)
|
||||
{
|
||||
bool successfull;
|
||||
|
||||
if(instance->config.logo.type == FF_LOGO_TYPE_BUILTIN)
|
||||
successfull = logoPrintBuiltinIfExists(instance, instance->config.logo.source.chars);
|
||||
else if(instance->config.logo.type == FF_LOGO_TYPE_FILE)
|
||||
successfull = logoPrintFileIfExists(instance, true);
|
||||
else if(instance->config.logo.type == FF_LOGO_TYPE_RAW)
|
||||
successfull = logoPrintFileIfExists(instance, false);
|
||||
else //image
|
||||
successfull = logoPrintImageIfExists(instance, instance->config.logo.type);
|
||||
|
||||
if(!successfull)
|
||||
logoPrintDetected(instance);
|
||||
}
|
||||
|
||||
void ffLogoPrint(FFinstance* instance)
|
||||
{
|
||||
//In pipe mode, we don't have a logo or padding.
|
||||
//We also don't need to set main color, because it won't be printed anyway.
|
||||
//So we can return quickly here.
|
||||
if(instance->config.pipe)
|
||||
{
|
||||
instance->state.logoHeight = 0;
|
||||
instance->state.logoWidth = 0;
|
||||
return;
|
||||
}
|
||||
|
||||
//If the source is not set, we can directly print the detected logo.
|
||||
if(instance->config.logo.source.length == 0)
|
||||
{
|
||||
logoPrintDetected(instance);
|
||||
return;
|
||||
}
|
||||
|
||||
//If the source and source type is set to something else than auto, always print with the set type.
|
||||
if(instance->config.logo.source.length > 0 && instance->config.logo.type != FF_LOGO_TYPE_AUTO)
|
||||
{
|
||||
logoPrintKnownType(instance);
|
||||
return;
|
||||
}
|
||||
|
||||
//If source matches the name of a builtin logo, print it and return.
|
||||
if(logoPrintBuiltinIfExists(instance, instance->config.logo.source.chars))
|
||||
return;
|
||||
|
||||
const FFTerminalShellResult* terminalShell = ffDetectTerminalShell(instance);
|
||||
|
||||
//Terminal emulators that support kitty graphics protocol.
|
||||
bool supportsKitty =
|
||||
ffStrbufIgnCaseCompS(&terminalShell->terminalProcessName, "kitty") == 0 ||
|
||||
ffStrbufIgnCaseCompS(&terminalShell->terminalProcessName, "konsole") == 0 ||
|
||||
ffStrbufIgnCaseCompS(&terminalShell->terminalProcessName, "wezterm") == 0 ||
|
||||
ffStrbufIgnCaseCompS(&terminalShell->terminalProcessName, "wayst") == 0;
|
||||
|
||||
//Try to load the logo as an image. If it succeeds, print it and return.
|
||||
if(logoPrintImageIfExists(instance, supportsKitty ? FF_LOGO_TYPE_KITTY : FF_LOGO_TYPE_CHAFA))
|
||||
return;
|
||||
|
||||
//Try to load the logo as a file. If it succeeds, print it and return.
|
||||
if(logoPrintFileIfExists(instance, true))
|
||||
return;
|
||||
|
||||
logoPrintDetected(instance);
|
||||
}
|
||||
|
||||
void ffLogoPrintLine(FFinstance* instance)
|
||||
{
|
||||
if(instance->state.logoWidth > 0)
|
||||
printf("\033[%uC", instance->state.logoWidth);
|
||||
|
||||
++instance->state.keysHeight;
|
||||
}
|
||||
|
||||
void ffLogoPrintRemaining(FFinstance* instance)
|
||||
{
|
||||
while(instance->state.keysHeight <= instance->state.logoHeight)
|
||||
{
|
||||
ffLogoPrintLine(instance);
|
||||
putchar('\n');
|
||||
}
|
||||
}
|
||||
|
||||
void ffLogoBuiltinPrint(FFinstance* instance)
|
||||
{
|
||||
GetLogoMethod* methods = ffLogoBuiltinGetAll();
|
||||
|
||||
while(*methods != NULL)
|
||||
{
|
||||
const FFlogo* logo = (*methods)();
|
||||
printf("\033[%sm%s:\033[0m\n", logo->builtinColors[0], logo->names[0]);
|
||||
logoPrintStruct(instance, logo);
|
||||
ffLogoPrintRemaining(instance);
|
||||
|
||||
//reset everything
|
||||
instance->state.logoHeight = 0;
|
||||
instance->state.keysHeight = 0;
|
||||
for(uint8_t i = 0; i < FASTFETCH_LOGO_MAX_COLORS; i++)
|
||||
ffStrbufClear(&instance->config.logo.colors[i]);
|
||||
|
||||
puts("\n");
|
||||
++methods;
|
||||
}
|
||||
}
|
||||
|
||||
void ffLogoBuiltinList()
|
||||
{
|
||||
GetLogoMethod* methods = ffLogoBuiltinGetAll();
|
||||
|
||||
uint32_t counter = 0;
|
||||
|
||||
while(*methods != NULL)
|
||||
{
|
||||
const FFlogo* logo = (*methods)();
|
||||
const char** names = logo->names;
|
||||
|
||||
printf("%u)%s ", counter, counter < 10 ? " " : "");
|
||||
++counter;
|
||||
|
||||
while(*names != NULL)
|
||||
{
|
||||
printf("\"%s\" ", *names);
|
||||
++names;
|
||||
}
|
||||
|
||||
putchar('\n');
|
||||
++methods;
|
||||
}
|
||||
}
|
||||
|
||||
void ffLogoBuiltinListAutocompletion()
|
||||
{
|
||||
GetLogoMethod* methods = ffLogoBuiltinGetAll();
|
||||
|
||||
while(*methods != NULL)
|
||||
{
|
||||
printf("%s\n", (*methods)()->names[0]);
|
||||
++methods;
|
||||
}
|
||||
}
|
||||
@@ -1,27 +0,0 @@
|
||||
#pragma once
|
||||
|
||||
#ifndef FASTFETCH_INCLUDED_LOGO_logo
|
||||
#define FASTFETCH_INCLUDED_LOGO_logo
|
||||
|
||||
#include "fastfetch.h"
|
||||
|
||||
typedef struct FFlogo
|
||||
{
|
||||
const char* data;
|
||||
const char** names; //Null terminated
|
||||
const char** builtinColors; //Null terminated
|
||||
} FFlogo;
|
||||
|
||||
typedef const FFlogo*(*GetLogoMethod)();
|
||||
|
||||
//logo.c
|
||||
void ffLogoPrintChars(FFinstance* instance, const char* data, bool doColorReplacement);
|
||||
|
||||
//builtin.c
|
||||
const FFlogo* ffLogoBuiltinGetUnknown();
|
||||
GetLogoMethod* ffLogoBuiltinGetAll();
|
||||
|
||||
//image/image.c
|
||||
bool ffLogoPrintImageIfExists(FFinstance* instance, FFLogoType type);
|
||||
|
||||
#endif
|
||||
@@ -1,182 +0,0 @@
|
||||
#include "fastfetch.h"
|
||||
#include "common/io.h"
|
||||
#include "common/printing.h"
|
||||
|
||||
#include <dirent.h>
|
||||
|
||||
#define FF_BATTERY_MODULE_NAME "Battery"
|
||||
#define FF_BATTERY_NUM_FORMAT_ARGS 5
|
||||
|
||||
typedef struct BatteryResult
|
||||
{
|
||||
FFstrbuf manufacturer;
|
||||
FFstrbuf modelName;
|
||||
FFstrbuf technology;
|
||||
FFstrbuf capacity;
|
||||
FFstrbuf status;
|
||||
} BatteryResult;
|
||||
|
||||
static void parseBattery(FFstrbuf* dir, FFlist* results)
|
||||
{
|
||||
uint32_t dirLength = dir->length;
|
||||
|
||||
FFstrbuf testBatteryBuffer;
|
||||
ffStrbufInit(&testBatteryBuffer);
|
||||
|
||||
//type must exist and be "Battery"
|
||||
ffStrbufAppendS(dir, "/type");
|
||||
ffReadFileBuffer(dir->chars, &testBatteryBuffer);
|
||||
ffStrbufSubstrBefore(dir, dirLength);
|
||||
|
||||
if(ffStrbufIgnCaseCompS(&testBatteryBuffer, "Battery") != 0)
|
||||
{
|
||||
ffStrbufDestroy(&testBatteryBuffer);
|
||||
return;
|
||||
}
|
||||
|
||||
//scope may not exist or must not be "Device"
|
||||
ffStrbufAppendS(dir, "/scope");
|
||||
ffReadFileBuffer(dir->chars, &testBatteryBuffer);
|
||||
ffStrbufSubstrBefore(dir, dirLength);
|
||||
|
||||
if(ffStrbufIgnCaseCompS(&testBatteryBuffer, "Device") == 0)
|
||||
{
|
||||
ffStrbufDestroy(&testBatteryBuffer);
|
||||
return;
|
||||
}
|
||||
|
||||
ffStrbufDestroy(&testBatteryBuffer);
|
||||
BatteryResult* result = ffListAdd(results);
|
||||
|
||||
//capacity must exist and be not empty
|
||||
ffStrbufInit(&result->capacity);
|
||||
ffStrbufAppendS(dir, "/capacity");
|
||||
ffReadFileBuffer(dir->chars, &result->capacity);
|
||||
ffStrbufSubstrBefore(dir, dirLength);
|
||||
|
||||
if(result->capacity.length == 0)
|
||||
{
|
||||
ffStrbufDestroy(&result->capacity);
|
||||
--results->length;
|
||||
return;
|
||||
}
|
||||
|
||||
//At this point, we have a battery. Try to get as much values as possible.
|
||||
|
||||
ffStrbufInit(&result->manufacturer);
|
||||
ffStrbufAppendS(dir, "/manufacturer");
|
||||
ffReadFileBuffer(dir->chars, &result->manufacturer);
|
||||
ffStrbufSubstrBefore(dir, dirLength);
|
||||
|
||||
ffStrbufInit(&result->modelName);
|
||||
ffStrbufAppendS(dir, "/model_name");
|
||||
ffReadFileBuffer(dir->chars, &result->modelName);
|
||||
ffStrbufSubstrBefore(dir, dirLength);
|
||||
|
||||
ffStrbufInit(&result->technology);
|
||||
ffStrbufAppendS(dir, "/technology");
|
||||
ffReadFileBuffer(dir->chars, &result->technology);
|
||||
ffStrbufSubstrBefore(dir, dirLength);
|
||||
|
||||
ffStrbufInit(&result->status);
|
||||
ffStrbufAppendS(dir, "/status");
|
||||
ffReadFileBuffer(dir->chars, &result->status);
|
||||
ffStrbufSubstrBefore(dir, dirLength);
|
||||
}
|
||||
|
||||
static void printBattery(FFinstance* instance, const BatteryResult* result, uint8_t index)
|
||||
{
|
||||
if(instance->config.battery.outputFormat.length == 0)
|
||||
{
|
||||
ffPrintLogoAndKey(instance, FF_BATTERY_MODULE_NAME, index, &instance->config.battery.key);
|
||||
|
||||
bool showStatus =
|
||||
result->status.length > 0 &&
|
||||
ffStrbufIgnCaseCompS(&result->status, "Unknown") != 0;
|
||||
|
||||
if(result->capacity.length > 0)
|
||||
{
|
||||
ffStrbufWriteTo(&result->capacity, stdout);
|
||||
putchar('%');
|
||||
|
||||
if(showStatus)
|
||||
fputs(" [", stdout);
|
||||
}
|
||||
|
||||
if(showStatus)
|
||||
{
|
||||
ffStrbufWriteTo(&result->status, stdout);
|
||||
|
||||
if(result->capacity.length > 0)
|
||||
putchar(']');
|
||||
}
|
||||
|
||||
putchar('\n');
|
||||
}
|
||||
else
|
||||
{
|
||||
ffPrintFormat(instance, FF_BATTERY_MODULE_NAME, index, &instance->config.battery, FF_BATTERY_NUM_FORMAT_ARGS, (FFformatarg[]){
|
||||
{FF_FORMAT_ARG_TYPE_STRBUF, &result->manufacturer},
|
||||
{FF_FORMAT_ARG_TYPE_STRBUF, &result->modelName},
|
||||
{FF_FORMAT_ARG_TYPE_STRBUF, &result->technology},
|
||||
{FF_FORMAT_ARG_TYPE_STRBUF, &result->capacity},
|
||||
{FF_FORMAT_ARG_TYPE_STRBUF, &result->status}
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
void ffPrintBattery(FFinstance* instance)
|
||||
{
|
||||
FFstrbuf baseDir;
|
||||
ffStrbufInitA(&baseDir, 64);
|
||||
if(instance->config.batteryDir.length > 0)
|
||||
{
|
||||
ffStrbufAppend(&baseDir, &instance->config.batteryDir);
|
||||
ffStrbufEndsWithC(&baseDir, '/');
|
||||
}
|
||||
else
|
||||
{
|
||||
ffStrbufAppendS(&baseDir, "/sys/class/power_supply/");
|
||||
}
|
||||
|
||||
uint32_t baseDirLength = baseDir.length;
|
||||
|
||||
DIR* dirp = opendir(baseDir.chars);
|
||||
if(dirp == NULL)
|
||||
{
|
||||
ffPrintError(instance, FF_BATTERY_MODULE_NAME, 0, &instance->config.battery, "opendir(\"%s\") == NULL", baseDir.chars);
|
||||
ffStrbufDestroy(&baseDir);
|
||||
return;
|
||||
}
|
||||
|
||||
FFlist results;
|
||||
ffListInitA(&results, sizeof(BatteryResult), 4);
|
||||
|
||||
struct dirent* entry;
|
||||
while((entry = readdir(dirp)) != NULL)
|
||||
{
|
||||
ffStrbufAppendS(&baseDir, entry->d_name);
|
||||
parseBattery(&baseDir, &results);
|
||||
ffStrbufSubstrBefore(&baseDir, baseDirLength);
|
||||
}
|
||||
|
||||
closedir(dirp);
|
||||
|
||||
for(uint8_t i = 0; i < (uint8_t) results.length; i++)
|
||||
{
|
||||
BatteryResult* result = ffListGet(&results, i);
|
||||
printBattery(instance, result, i);
|
||||
|
||||
ffStrbufDestroy(&result->manufacturer);
|
||||
ffStrbufDestroy(&result->modelName);
|
||||
ffStrbufDestroy(&result->technology);
|
||||
ffStrbufDestroy(&result->capacity);
|
||||
ffStrbufDestroy(&result->status);
|
||||
}
|
||||
|
||||
if(results.length == 0)
|
||||
ffPrintError(instance, FF_BATTERY_MODULE_NAME, 0, &instance->config.battery, "%s doesn't contain any battery folder", baseDir.chars);
|
||||
|
||||
ffListDestroy(&results);
|
||||
ffStrbufDestroy(&baseDir);
|
||||
}
|
||||
@@ -1,8 +0,0 @@
|
||||
#include "fastfetch.h"
|
||||
#include "common/printing.h"
|
||||
|
||||
void ffPrintBreak(FFinstance* instance)
|
||||
{
|
||||
ffLogoPrintLine(instance);
|
||||
putchar('\n');
|
||||
}
|
||||
@@ -1,35 +0,0 @@
|
||||
#include "fastfetch.h"
|
||||
#include "common/printing.h"
|
||||
|
||||
void ffPrintColors(FFinstance* instance)
|
||||
{
|
||||
if(instance->config.pipe)
|
||||
{
|
||||
ffLogoPrintLine(instance);
|
||||
puts("████████████████████████");
|
||||
ffLogoPrintLine(instance);
|
||||
puts("████████████████████████");
|
||||
return;
|
||||
}
|
||||
|
||||
ffLogoPrintLine(instance);
|
||||
|
||||
// 4%d: Set the background color
|
||||
// 3%d: Set the foreground color
|
||||
for(uint8_t i = 0; i < 8; i++)
|
||||
printf("\033[4%d;3%dm███", i, i);
|
||||
|
||||
puts(FASTFETCH_TEXT_MODIFIER_RESET);
|
||||
|
||||
ffLogoPrintLine(instance);
|
||||
|
||||
// 1: Set everything to bolt. This causes normal colors on some systems to be bright.
|
||||
// 4%d: Set the backgound to the not bright color
|
||||
// 3%d: Set the foreground to the not bright color
|
||||
// 10%d: Set the background to the bright color
|
||||
// 9%d: Set the foreground to the bright color
|
||||
for(uint8_t i = 0; i < 8; i++)
|
||||
printf("\033[1;4%d;3%d;10%d;9%dm███", i, i, i, i);
|
||||
|
||||
puts(FASTFETCH_TEXT_MODIFIER_RESET);
|
||||
}
|
||||
@@ -1,213 +0,0 @@
|
||||
#include "fastfetch.h"
|
||||
#include "common/io.h"
|
||||
#include "common/properties.h"
|
||||
#include "common/printing.h"
|
||||
#include "common/caching.h"
|
||||
#include "detection/temps.h"
|
||||
|
||||
#include <stdlib.h>
|
||||
|
||||
#define FF_CPU_MODULE_NAME "CPU"
|
||||
#define FF_CPU_NUM_FORMAT_ARGS 15
|
||||
|
||||
static double getGhz(const char* policyFile, const char* cpuFile)
|
||||
{
|
||||
FFstrbuf content;
|
||||
ffStrbufInit(&content);
|
||||
|
||||
ffReadFileBuffer(policyFile, &content);
|
||||
if(content.length == 0)
|
||||
ffReadFileBuffer(cpuFile, &content);
|
||||
|
||||
double herz = ffStrbufToDouble(&content);
|
||||
|
||||
ffStrbufDestroy(&content);
|
||||
|
||||
//ffStrbufToDouble failed
|
||||
if(herz != herz)
|
||||
return 0;
|
||||
|
||||
herz /= 1000.0; //to MHz
|
||||
return herz / 1000.0; //to GHz
|
||||
}
|
||||
|
||||
static double detectCPUTemp(const FFinstance* instance)
|
||||
{
|
||||
const FFTempsResult *temps = ffDetectTemps(instance);
|
||||
|
||||
for(uint32_t i = 0; i < temps->values.length; i++)
|
||||
{
|
||||
FFTempValue* value = ffListGet(&temps->values, i);
|
||||
|
||||
if(
|
||||
ffStrbufFirstIndexS(&value->name, "cpu") < value->name.length ||
|
||||
ffStrbufCompS(&value->name, "k10temp") == 0 ||
|
||||
ffStrbufCompS(&value->name, "coretemp") == 0
|
||||
) return value->value;
|
||||
}
|
||||
|
||||
return 0.0 / 0.0; //NaN
|
||||
}
|
||||
|
||||
void ffPrintCPU(FFinstance* instance)
|
||||
{
|
||||
if(ffPrintFromCache(instance, FF_CPU_MODULE_NAME, &instance->config.cpu, FF_CPU_NUM_FORMAT_ARGS))
|
||||
return;
|
||||
|
||||
FILE* cpuinfo = fopen("/proc/cpuinfo", "r");
|
||||
if(cpuinfo == NULL)
|
||||
{
|
||||
ffPrintError(instance, FF_CPU_MODULE_NAME, 0, &instance->config.cpu, "fopen(\"""/proc/cpuinfo\", \"r\") == NULL");
|
||||
return;
|
||||
}
|
||||
|
||||
FFstrbuf name;
|
||||
ffStrbufInitA(&name, 64);
|
||||
|
||||
FFstrbuf vendor;
|
||||
ffStrbufInitA(&vendor, 64);
|
||||
|
||||
FFstrbuf physicalCoresString;
|
||||
ffStrbufInit(&physicalCoresString);
|
||||
|
||||
FFstrbuf procGhzString;
|
||||
ffStrbufInit(&procGhzString);
|
||||
|
||||
char* line = NULL;
|
||||
size_t len = 0;
|
||||
|
||||
while(getline(&line, &len, cpuinfo) != -1)
|
||||
{
|
||||
//Stop after the first CPU
|
||||
if(name.length > 0 && (*line == '\0' || *line == '\n'))
|
||||
break;
|
||||
|
||||
(void)(
|
||||
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
|
||||
);
|
||||
}
|
||||
|
||||
if(line != NULL)
|
||||
free(line);
|
||||
|
||||
fclose(cpuinfo);
|
||||
|
||||
double procGhz = ffStrbufToDouble(&procGhzString);
|
||||
if(procGhz != procGhz)
|
||||
procGhz = 0; //NaN
|
||||
else
|
||||
procGhz /= 1000.0; //To GHz
|
||||
|
||||
ffStrbufDestroy(&procGhzString);
|
||||
|
||||
double biosLimit = getGhz("/sys/devices/system/cpu/cpufreq/policy0/bios_limit", "/sys/devices/system/cpu/cpu0/cpufreq/bios_limit");
|
||||
double scalingMaxFreq = getGhz("/sys/devices/system/cpu/cpufreq/policy0/scaling_max_freq", "/sys/devices/system/cpu/cpu0/cpufreq/scaling_max_freq");
|
||||
double scalingMinFreq = getGhz("/sys/devices/system/cpu/cpufreq/policy0/scaling_min_freq", "/sys/devices/system/cpu/cpu0/cpufreq/scaling_min_freq");
|
||||
double infoMaxFreq = getGhz("/sys/devices/system/cpu/cpufreq/policy0/cpuinfo_max_freq", "/sys/devices/system/cpu/cpu0/cpufreq/cpuinfo_max_freq");
|
||||
double infoMinFreq = getGhz("/sys/devices/system/cpu/cpufreq/policy0/cpuinfo_min_freq", "/sys/devices/system/cpu/cpu0/cpufreq/cpuinfo_min_freq");
|
||||
|
||||
int numProcsOnline = get_nprocs();
|
||||
int numProcsAvailable = get_nprocs_conf();
|
||||
|
||||
int physicalCores = 1;
|
||||
sscanf(physicalCoresString.chars, "%i", &physicalCores);
|
||||
ffStrbufDestroy(&physicalCoresString);
|
||||
|
||||
//The current get_nprocs* returns 1 on failure. It also makes no sense to have a (1) as count
|
||||
int numProcs = numProcsOnline;
|
||||
if(numProcs <= 1)
|
||||
numProcs = numProcsAvailable;
|
||||
if(numProcs <= 1)
|
||||
numProcs = physicalCores;
|
||||
|
||||
double ghz = biosLimit;
|
||||
if(ghz == 0)
|
||||
ghz = scalingMaxFreq;
|
||||
if(ghz == 0)
|
||||
ghz = infoMaxFreq;
|
||||
if(ghz == 0)
|
||||
ghz = procGhz;
|
||||
if(ghz == 0)
|
||||
ghz = scalingMinFreq;
|
||||
if(ghz == 0)
|
||||
ghz = infoMinFreq;
|
||||
|
||||
if(
|
||||
name.length == 0 &&
|
||||
vendor.length == 0 &&
|
||||
numProcs <= 1 &&
|
||||
ghz <= 0
|
||||
) {
|
||||
ffStrbufDestroy(&name);
|
||||
ffStrbufDestroy(&vendor);
|
||||
ffPrintError(instance, FF_CPU_MODULE_NAME, 0, &instance->config.cpu, "No CPU info found in /proc/cpuinfo");
|
||||
return;
|
||||
}
|
||||
|
||||
FFstrbuf namePretty;
|
||||
ffStrbufInitA(&namePretty, 64);
|
||||
ffStrbufAppend(&namePretty, &name);
|
||||
|
||||
const char* removeStrings[] = {
|
||||
"(R)", "(r)", "(TM)", "(tm)",
|
||||
" CPU", " FPU", " APU", " Processor",
|
||||
" Dual-Core", " Quad-Core", " Six-Core", " Eight-Core", " Ten-Core",
|
||||
" 2-Core", " 4-Core", " 6-Core", " 8-Core", " 10-Core", " 12-Core", " 14-Core", " 16-Core"
|
||||
};
|
||||
|
||||
ffStrbufRemoveStringsA(&namePretty, sizeof(removeStrings) / sizeof(removeStrings[0]), removeStrings);
|
||||
ffStrbufSubstrBeforeFirstC(&namePretty, '@'); //Cut the speed output in the name as we append our own
|
||||
ffStrbufTrimRight(&namePretty, ' '); //If we removed the @ in previous step there was most likely a space before it
|
||||
|
||||
double cpuTemp;
|
||||
if(instance->config.cpu.outputFormat.length > 0)
|
||||
cpuTemp = detectCPUTemp(instance);
|
||||
|
||||
FFstrbuf cpu;
|
||||
ffStrbufInitA(&cpu, 128);
|
||||
|
||||
if(namePretty.length > 0)
|
||||
ffStrbufAppend(&cpu, &namePretty);
|
||||
else if(name.length > 0)
|
||||
ffStrbufAppend(&cpu, &name);
|
||||
else if(vendor.length > 0)
|
||||
{
|
||||
ffStrbufAppend(&cpu, &vendor);
|
||||
ffStrbufAppendS(&cpu, " CPU");
|
||||
}
|
||||
else
|
||||
ffStrbufAppendS(&cpu, "CPU");
|
||||
|
||||
if(numProcs > 1)
|
||||
ffStrbufAppendF(&cpu, " (%i)", numProcs);
|
||||
|
||||
if(ghz > 0)
|
||||
ffStrbufAppendF(&cpu, " @ %.9gGHz", ghz);
|
||||
|
||||
ffPrintAndWriteToCache(instance, FF_CPU_MODULE_NAME, &instance->config.cpu, &cpu, FF_CPU_NUM_FORMAT_ARGS, (FFformatarg[]){
|
||||
{FF_FORMAT_ARG_TYPE_STRBUF, &name},
|
||||
{FF_FORMAT_ARG_TYPE_STRBUF, &namePretty},
|
||||
{FF_FORMAT_ARG_TYPE_STRBUF, &vendor},
|
||||
{FF_FORMAT_ARG_TYPE_INT, &numProcsOnline},
|
||||
{FF_FORMAT_ARG_TYPE_INT, &numProcsAvailable},
|
||||
{FF_FORMAT_ARG_TYPE_INT, &physicalCores},
|
||||
{FF_FORMAT_ARG_TYPE_INT, &numProcs},
|
||||
{FF_FORMAT_ARG_TYPE_DOUBLE, &cpuTemp},
|
||||
{FF_FORMAT_ARG_TYPE_DOUBLE, &biosLimit},
|
||||
{FF_FORMAT_ARG_TYPE_DOUBLE, &scalingMaxFreq},
|
||||
{FF_FORMAT_ARG_TYPE_DOUBLE, &scalingMinFreq},
|
||||
{FF_FORMAT_ARG_TYPE_DOUBLE, &infoMaxFreq},
|
||||
{FF_FORMAT_ARG_TYPE_DOUBLE, &infoMinFreq},
|
||||
{FF_FORMAT_ARG_TYPE_DOUBLE, &procGhz},
|
||||
{FF_FORMAT_ARG_TYPE_DOUBLE, &ghz}
|
||||
});
|
||||
|
||||
ffStrbufDestroy(&cpu);
|
||||
ffStrbufDestroy(&namePretty);
|
||||
ffStrbufDestroy(&name);
|
||||
ffStrbufDestroy(&vendor);
|
||||
}
|
||||
@@ -1,49 +0,0 @@
|
||||
#include "fastfetch.h"
|
||||
#include "common/printing.h"
|
||||
|
||||
#include <unistd.h>
|
||||
|
||||
#define FF_CPU_USAGE_MODULE_NAME "CPU Usage"
|
||||
#define FF_CPU_USAGE_NUM_FORMAT_ARGS 1
|
||||
|
||||
void ffPrintCPUUsage(FFinstance* instance)
|
||||
{
|
||||
long user, nice, system, idle, iowait, irq, softirq;
|
||||
FILE* procStat = fopen("/proc/stat", "r");
|
||||
if(procStat == NULL)
|
||||
{
|
||||
ffPrintError(instance, FF_CPU_USAGE_MODULE_NAME, 0, &instance->config.cpu, "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;
|
||||
long workJiffies1 = user + nice + system;
|
||||
long totalJiffies1 = workJiffies1 + idle + iowait + irq + softirq;
|
||||
|
||||
sleep(1);
|
||||
|
||||
rewind(procStat);
|
||||
if (fscanf(procStat, "cpu%ld%ld%ld%ld%ld%ld%ld", &user, &nice, &system, &idle, &iowait, &irq, &softirq) < 0) goto exit;
|
||||
long workJiffies2 = user + nice + system;
|
||||
long totalJiffies2 = workJiffies2 + idle + iowait + irq + softirq;
|
||||
|
||||
// https://stackoverflow.com/questions/3017162/how-to-get-total-cpu-usage-in-linux-using-c#answer-3017438
|
||||
long workOverPeriod = workJiffies2 - workJiffies1;
|
||||
long totalOverPeriod = totalJiffies2 - totalJiffies1;
|
||||
double cpuPercent = (double)workOverPeriod / (double)totalOverPeriod * 100;
|
||||
|
||||
if(instance->config.cpuUsage.outputFormat.length == 0)
|
||||
{
|
||||
ffPrintLogoAndKey(instance, FF_CPU_USAGE_MODULE_NAME, 0, &instance->config.cpuUsage.key);
|
||||
|
||||
printf("%.2lf%%\n", cpuPercent);
|
||||
}
|
||||
else
|
||||
{
|
||||
ffPrintFormat(instance, FF_CPU_USAGE_MODULE_NAME, 0, &instance->config.cpuUsage, FF_CPU_USAGE_NUM_FORMAT_ARGS, (FFformatarg[]){
|
||||
{FF_FORMAT_ARG_TYPE_DOUBLE, &cpuPercent}
|
||||
});
|
||||
}
|
||||
|
||||
exit:
|
||||
fclose(procStat);
|
||||
}
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user