diff --git a/.github/ISSUE_TEMPLATE/bug_report.md b/.github/ISSUE_TEMPLATE/bug_report.md
index 5492b9ea2..0fb775db3 100644
--- a/.github/ISSUE_TEMPLATE/bug_report.md
+++ b/.github/ISSUE_TEMPLATE/bug_report.md
@@ -39,5 +39,5 @@ Output of `fastfetch --list-features`:
diff --git a/.github/workflows/push.yml b/.github/workflows/push.yml
index ae492a408..fc9935eb9 100644
--- a/.github/workflows/push.yml
+++ b/.github/workflows/push.yml
@@ -35,17 +35,17 @@ jobs:
uses: github/codeql-action/analyze@v2
- name: run fastfetch
- run: ./fastfetch --recache --disable-linewrap false --hide-cursor false --show-errors true --load-config presets/all
+ run: time ./fastfetch --disable-linewrap false --hide-cursor false --show-errors true --load-config presets/all
- name: run flashfetch
- run: ./flashfetch
+ run: time ./flashfetch
- name: run tests
run: ctest
- name: get fastfetch version
id: ffversion
- run: echo "::set-output name=ffversion::$(./fastfetch --version-raw)"
+ run: echo "ffversion=$(./fastfetch --version-raw)" >> $GITHUB_OUTPUT
- name: upload artifacts
uses: actions/upload-artifact@v3
@@ -64,7 +64,7 @@ jobs:
uses: actions/checkout@v3
- name: install required packages
- run: brew install vulkan-loader molten-vk
+ run: brew install vulkan-loader molten-vk imagemagick chafa
- name: Initialize CodeQL
uses: github/codeql-action/init@v2
@@ -81,10 +81,10 @@ jobs:
uses: github/codeql-action/analyze@v2
- name: run fastfetch
- run: ./fastfetch --recache --disable-linewrap false --hide-cursor false --show-errors true --load-config presets/all
+ run: time ./fastfetch --disable-linewrap false --hide-cursor false --show-errors true --load-config presets/all
- name: run flashfetch
- run: ./flashfetch
+ run: time ./flashfetch
- name: run tests
run: ctest
@@ -95,8 +95,8 @@ jobs:
name: fastfetch-macos
path: ./fastfetch-*.*
- bsd:
- name: BSD
+ freebsd:
+ name: FreeBSD
runs-on: macos-12
permissions:
security-events: write
@@ -113,16 +113,164 @@ jobs:
run: |
cmake -DSET_TWEAK=Off -DBUILD_TESTS=On .
cmake --build . --target package
- ./fastfetch --recache --disable-linewrap false --hide-cursor false --show-errors true --load-config presets/all
- ./flashfetch
+ time ./fastfetch --disable-linewrap false --hide-cursor false --show-errors true --load-config presets/all
+ time ./flashfetch
ctest
- name: upload artifacts
uses: actions/upload-artifact@v3
with:
- name: fastfetch-bsd
+ name: fastfetch-freebsd
path: ./fastfetch-*.*
+ windows:
+ name: Windows
+ runs-on: windows-latest
+ permissions:
+ security-events: write
+ contents: read
+ defaults:
+ run:
+ shell: msys2 {0}
+ steps:
+ - name: checkout repository
+ uses: actions/checkout@v3
+
+ - name: setup-msys2
+ uses: msys2/setup-msys2@v2
+ with:
+ msystem: CLANG64
+ update: true
+ install: git mingw-w64-clang-x86_64-cmake mingw-w64-clang-x86_64-clang mingw-w64-clang-x86_64-cjson mingw-w64-clang-x86_64-vulkan-loader mingw-w64-clang-x86_64-opencl-icd
+
+ - name: print msys version
+ run: uname -a
+
+ # https://github.com/msys2/MINGW-packages/issues/13524#event-7555720785
+ - name: create OpenCL.pc
+ run: |
+ cat > /clang64/lib/pkgconfig/OpenCL.pc << EOF
+ prefix=/clang64
+ exec_prefix=${prefix}
+ libdir=${exec_prefix}/lib
+ includedir=${prefix}/include
+
+ Name: OpenCL
+ Description: Open Computing Language generic Installable Client Driver Loader
+ Version: 2022.09.30-1
+ Libs: -L${libdir} -lOpenCL.dll
+ Cflags: -I${includedir}
+ EOF
+
+ - name: Initialize CodeQL
+ uses: github/codeql-action/init@v2
+ with:
+ languages: c, cpp
+
+ - name: configure project
+ run: env PKG_CONFIG_PATH=/clang64/lib/pkgconfig/:$PKG_CONFIG_PATH cmake -DSET_TWEAK=Off -DBUILD_TESTS=On .
+
+ - name: build project
+ run: cmake --build .
+
+ - name: perform CodeQL analysis
+ uses: github/codeql-action/analyze@v2
+
+ - name: copy necessary dlls
+ run: cp /clang64/bin/{libcjson,libOpenCL,vulkan-1}.dll .
+
+ - name: run fastfetch
+ run: time ./fastfetch --disable-linewrap false --hide-cursor false --show-errors true --load-config presets/all
+
+ - name: run flashfetch
+ run: time ./flashfetch
+
+ - name: run tests
+ run: ctest
+
+ - name: upload artifacts
+ uses: actions/upload-artifact@v3
+ with:
+ name: fastfetch-windows
+ path: |
+ ./*.dll
+ ./fastfetch.exe
+ ./flashfetch.exe
+
+ windows-old:
+ name: Windows-old
+ runs-on: windows-latest
+ permissions:
+ security-events: write
+ contents: read
+ defaults:
+ run:
+ shell: msys2 {0}
+ steps:
+ - name: checkout repository
+ uses: actions/checkout@v3
+
+ - name: setup-msys2
+ uses: msys2/setup-msys2@v2
+ with:
+ msystem: MINGW64
+ update: true
+ install: git mingw-w64-x86_64-cmake mingw-w64-x86_64-gcc mingw-w64-x86_64-cjson mingw-w64-x86_64-vulkan-loader mingw-w64-x86_64-opencl-icd
+
+ - name: print msys version
+ run: uname -a
+
+ # https://github.com/msys2/MINGW-packages/issues/13524#event-7555720785
+ - name: create OpenCL.pc
+ run: |
+ cat > /mingw64/lib/pkgconfig/OpenCL.pc << EOF
+ prefix=/mingw64
+ exec_prefix=${prefix}
+ libdir=${exec_prefix}/lib
+ includedir=${prefix}/include
+
+ Name: OpenCL
+ Description: Open Computing Language generic Installable Client Driver Loader
+ Version: 2022.09.30-1
+ Libs: -L${libdir} -lOpenCL.dll
+ Cflags: -I${includedir}
+ EOF
+
+ - name: Initialize CodeQL
+ uses: github/codeql-action/init@v2
+ with:
+ languages: c, cpp
+
+ - name: configure project
+ run: env PKG_CONFIG_PATH=/mingw64/lib/pkgconfig/:$PKG_CONFIG_PATH cmake -DSET_TWEAK=Off -DBUILD_TESTS=On -DENABLE_BUFFER=Off .
+
+ - name: build project
+ run: cmake --build .
+
+ - name: perform CodeQL analysis
+ uses: github/codeql-action/analyze@v2
+
+ - name: copy necessary dlls
+ run: cp /mingw64/bin/{libcjson,libOpenCL,vulkan-1}.dll .
+
+ - name: run fastfetch
+ run: time ./fastfetch --disable-linewrap false --hide-cursor false --show-errors true --load-config presets/all
+
+ - name: run flashfetch
+ run: time ./flashfetch
+
+ - name: run tests
+ run: ctest
+
+ - name: upload artifacts
+ uses: actions/upload-artifact@v3
+ with:
+ name: fastfetch-windows-old
+ path: |
+ ./*.dll
+ ./fastfetch.exe
+ ./flashfetch.exe
+
release:
if: github.event_name == 'push' && github.ref == 'refs/heads/master' && github.repository == 'LinusDierheimer/fastfetch'
name: Release
@@ -130,7 +278,8 @@ jobs:
needs:
- linux
- macos
- - bsd
+ - freebsd
+ - windows
permissions:
contents: write
steps:
@@ -150,4 +299,4 @@ jobs:
tag: ${{ needs.linux.outputs.ffversion }}
commit: ${{ github.sha }}
artifactErrorsFailBuild: true
- artifacts: fastfetch-linux/*,fastfetch-macos/*,fastfetch-bsd/*
+ artifacts: fastfetch-linux/*,fastfetch-macos/*,fastfetch-freebsd/*,fastfetch-windows/*
diff --git a/.gitignore b/.gitignore
index f604d255a..94a0919c6 100644
--- a/.gitignore
+++ b/.gitignore
@@ -3,6 +3,7 @@
**/.cache/
**/.kdev4/
**/.DS_Store
+/.vs
cscope.*
tags
fastfetch.kdev4
diff --git a/CHANGELOG.md b/CHANGELOG.md
index c91d6efa5..e77568852 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -1,3 +1,59 @@
+# 1.8.0
+
+This release introduces Windows support! Fastfetch now fully support all major desktop OSes (Linux, macOS, Windows and FreeBSD)
+
+Notable Changes:
+* Bios / Board / Chassis modules are splitted against Host module for performance reasons
+* Caching is removed. Option `--nocache` is removed accordingly
+
+Features:
+* Windows (7 and newer) is officially and fully supported
+* FreeBSD support is improved greatly (Cpu Temp, Cpu Usage, Disk, Host, Processes, Swap, Terminal / Shell, Uptime)
+* Adds a new flag `--stat`, which prints time usage for individual modules
+* Adds Wifi module which supports Windows and macOS
+* Adds data source option for logo printing
+* Detects Homebrew Cellar and Cask seperately
+* Detects WSL version
+* Detects disk based on mount point
+* Exposes more chafa configs
+* Improves performance for Cpu Usage, Public IP, Weather modules
+* Improves performance for Kitty image protocol when both image width / height specified
+* Improves performance for large file loading
+* Improves performance for macOS WM and Host detection
+* Improves shell and terminal detection on macOS
+* Supports Deepin Terminal terminal font
+* Supports GPU detection on Android
+* Supports Kitty Terminal terminal font
+* Supports bar output for percentage values
+* Supports eopkg package manager detection
+* Supports iTerm image logo protocol
+* Supports image logo printing on macOS
+* Supports tcsh version detection
+* Vulkan module on macOS no longer requires vulkan-loader to work
+
+Logos:
+* Alpine
+* CRUX
+* EndeavourOS
+* Enso
+* Garuda small
+* Nobara
+* OpenMandriva
+* Parabola GNU/Linux-libre
+* Rocky
+* Rosa
+* Solus
+* Univalent
+* Vanilla OS
+
+Bugfixes:
+* Fixes disk size detection on 32bit Linux (#337)
+* Fixes cpu freq detection in WSL
+* Fixes internal bug of FFstrbuf
+* Fixes some memory leaks
+* Fixes segfault if 0 is given as argument index
+* Lots of code refactors
+
# 1.7.5
Fixes a crash on linux that could happen when getting zsh version (#285)
diff --git a/CMakeLists.txt b/CMakeLists.txt
index 59f624765..7f65fca70 100644
--- a/CMakeLists.txt
+++ b/CMakeLists.txt
@@ -7,18 +7,39 @@ project(fastfetch
HOMEPAGE_URL "https://github.com/LinusDierheimer/fastfetch"
)
+set(PROJECT_LICENSE "MIT license")
+
###################
# Target Platform #
###################
-
-if("${CMAKE_SYSTEM_NAME}" MATCHES ".*[Ll]inux.*")
+if(ANDROID)
+ set(LINUX FALSE)
+elseif("${CMAKE_SYSTEM_NAME}" MATCHES ".*[Ll]inux.*")
set(LINUX TRUE CACHE BOOL "..." FORCE) # LINUX means GNU/Linux, not just the kernel
elseif("${CMAKE_SYSTEM_NAME}" MATCHES ".*[Bb][Ss][Dd].*")
set(BSD TRUE CACHE BOOL "..." FORCE)
-elseif(NOT APPLE AND NOT ANDROID)
- message(FATAL_ERROR "Unsupported platform")
+elseif(NOT APPLE AND NOT WIN32)
+ message(FATAL_ERROR "Unsupported platform: ${CMAKE_SYSTEM_NAME}")
endif()
+if(WIN32)
+ enable_language(CXX)
+endif()
+
+#############################
+# Compile time dependencies #
+#############################
+
+set(THREADS_PREFER_PTHREAD_FLAG NOT WIN32)
+find_package(Threads)
+
+find_package(PkgConfig)
+if(NOT PKG_CONFIG_FOUND)
+ message(WARNING "pkg-config not found, library detection might be limited")
+endif()
+
+include(CheckIncludeFile)
+
#####################
# Configure options #
#####################
@@ -26,7 +47,7 @@ endif()
include(CMakeDependentOption)
cmake_dependent_option(ENABLE_LIBPCI "Enable libpci" ON "LINUX OR BSD" OFF)
-cmake_dependent_option(ENABLE_VULKAN "Enable vulkan" ON "LINUX OR APPLE OR BSD" OFF)
+cmake_dependent_option(ENABLE_VULKAN "Enable vulkan" ON "LINUX OR APPLE OR BSD OR WIN32 OR ANDROID" OFF)
cmake_dependent_option(ENABLE_WAYLAND "Enable wayland-client" ON "LINUX OR BSD" OFF)
cmake_dependent_option(ENABLE_XCB_RANDR "Enable xcb-randr" ON "LINUX OR BSD" OFF)
cmake_dependent_option(ENABLE_XCB "Enable xcb" ON "LINUX OR BSD" OFF)
@@ -38,31 +59,24 @@ cmake_dependent_option(ENABLE_DBUS "Enable dbus-1" ON "LINUX OR BSD" OFF)
cmake_dependent_option(ENABLE_XFCONF "Enable libxfconf-0" ON "LINUX OR BSD" OFF)
cmake_dependent_option(ENABLE_SQLITE3 "Enable sqlite3" ON "LINUX OR BSD" OFF)
cmake_dependent_option(ENABLE_RPM "Enable rpm" ON "LINUX" OFF)
-cmake_dependent_option(ENABLE_IMAGEMAGICK7 "Enable imagemagick 7" ON "LINUX OR BSD" OFF)
-cmake_dependent_option(ENABLE_IMAGEMAGICK6 "Enable imagemagick 6" ON "LINUX OR BSD" OFF)
+cmake_dependent_option(ENABLE_IMAGEMAGICK7 "Enable imagemagick 7" ON "LINUX OR BSD OR APPLE OR WIN32" OFF)
+cmake_dependent_option(ENABLE_IMAGEMAGICK6 "Enable imagemagick 6" ON "LINUX OR BSD OR APPLE" OFF)
cmake_dependent_option(ENABLE_CHAFA "Enable chafa" ON "ENABLE_IMAGEMAGICK6 OR ENABLE_IMAGEMAGICK7" OFF)
cmake_dependent_option(ENABLE_ZLIB "Enable zlib" ON "ENABLE_IMAGEMAGICK6 OR ENABLE_IMAGEMAGICK7" OFF)
cmake_dependent_option(ENABLE_EGL "Enable egl" ON "LINUX OR BSD" OFF)
cmake_dependent_option(ENABLE_GLX "Enable glx" ON "LINUX OR BSD" OFF)
cmake_dependent_option(ENABLE_OSMESA "Enable osmesa" ON "LINUX OR BSD" OFF)
-cmake_dependent_option(ENABLE_OPENCL "Enable opencl" ON "LINUX OR BSD" OFF)
-cmake_dependent_option(ENABLE_LIBCJSON "Enable libcjson" ON "LINUX" OFF)
+cmake_dependent_option(ENABLE_OPENCL "Enable opencl" ON "LINUX OR BSD OR WIN32" OFF)
+cmake_dependent_option(ENABLE_LIBCJSON "Enable libcjson" ON "LINUX OR WIN32" OFF)
cmake_dependent_option(ENABLE_FREETYPE "Enable freetype" ON "ANDROID" OFF)
+cmake_dependent_option(ENABLE_THREADS "Enable multithreading" ON "Threads_FOUND AND NOT ANDROID" OFF)
+cmake_dependent_option(ENABLE_BUFFER "Enable stdout buffer" ON "LINUX OR APPLE OR BSD OR WIN32 OR ANDROID" OFF)
+cmake_dependent_option(USE_WIN_NTAPI "Allow using internal NTAPI" ON "WIN32" OFF)
+cmake_dependent_option(USE_WIN_GPU_DXGI "Use DXGI to detect GPUs instead of WMI. Faster, but may ignore GPUs that only support DX9" ON "WIN32" OFF)
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)
-
-include(CheckIncludeFile)
-
####################
# Compiler options #
####################
@@ -72,24 +86,43 @@ if(NOT CMAKE_BUILD_TYPE)
endif()
message(STATUS "Build type: ${CMAKE_BUILD_TYPE}")
+if(ENABLE_THREADS)
+ if(CMAKE_USE_WIN32_THREADS_INIT)
+ message(STATUS "Threads type: Win32 thread")
+ elseif(CMAKE_USE_PTHREADS_INIT)
+ message(STATUS "Threads type: pthread")
+ endif()
+else()
+ message(STATUS "Threads type: disabled")
+endif()
set(CMAKE_C_STANDARD 11)
set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -Wall -Wextra -Wconversion")
+if(WIN32)
+ set(CMAKE_CXX_STANDARD 11)
+ set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -Wall -Wextra -Wconversion -fno-exceptions -fno-rtti")
+endif()
+
# Used for dlopen finding dylibs installed by homebrew
# `/opt/homebrew/lib` is not on in dlopen search path by default
if(APPLE AND DEFINED ENV{HOMEBREW_PREFIX})
set(CMAKE_EXE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS} -Wl,-rpath,$ENV{HOMEBREW_PREFIX}/lib")
endif()
-set(FASTFETCH_FLAGS_DEBUG "-fno-omit-frame-pointer -fsanitize=address -fsanitize=undefined")
+set(FASTFETCH_FLAGS_DEBUG "-fno-omit-frame-pointer")
+if(NOT WIN32)
+ set(FASTFETCH_FLAGS_DEBUG "${FASTFETCH_FLAGS_DEBUG} -fsanitize=address -fsanitize=undefined")
+endif()
set(CMAKE_C_FLAGS_DEBUG "${CMAKE_C_FLAGS_DEBUG} ${FASTFETCH_FLAGS_DEBUG}")
set(CMAKE_LINKER_FLAGS_DEBUG "${CMAKE_LINKER_FLAGS_DEBUG} ${FASTFETCH_FLAGS_DEBUG} -rdynamic")
-include(CheckIPOSupported)
-check_ipo_supported(RESULT IPO_SUPPORTED)
-if(IPO_SUPPORTED)
- set(CMAKE_INTERPROCEDURAL_OPTIMIZATION TRUE)
+if(NOT CMAKE_BUILD_TYPE STREQUAL "Debug")
+ include(CheckIPOSupported)
+ check_ipo_supported(RESULT IPO_SUPPORTED)
+ if(IPO_SUPPORTED)
+ set(CMAKE_INTERPROCEDURAL_OPTIMIZATION TRUE)
+ endif()
endif()
#######################
@@ -148,6 +181,11 @@ if (SET_TWEAK AND EXISTS "${CMAKE_SOURCE_DIR}/.git")
)
string(REGEX MATCH "-[0-9]+" PROJECT_VERSION_TWEAK "${PROJECT_VERSION_TWEAK}")
endif()
+if(PROJECT_VERSION_TWEAK)
+ string(REGEX MATCH "[0-9]+" PROJECT_VERSION_TWEAK_NUM "${PROJECT_VERSION_TWEAK}")
+else()
+ set(PROJECT_VERSION_TWEAK_NUM 0)
+endif()
#############
# Text data #
@@ -182,165 +220,274 @@ configure_file(src/fastfetch_config.h.in fastfetch_config.h)
#######################
set(LIBFASTFETCH_SRC
- src/util/FFstrbuf.c
- src/util/FFlist.c
- src/util/FFvaluestore.c
+ src/common/bar.c
+ src/common/font.c
+ src/common/format.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/qt.c
- src/detection/gtk.c
- src/detection/terminalShell.c
- src/detection/vulkan.c
- src/detection/datetime.c
- src/detection/title.c
- src/detection/host/host.c
- src/detection/os/os.c
+ src/common/parsing.c
+ src/common/printing.c
+ src/common/properties.c
+ src/common/settings.c
src/detection/cpu/cpu.c
- src/detection/gpu/gpu.c
- src/detection/memory/memory.c
- src/detection/font/font.c
+ src/detection/cpuUsage/cpuUsage.c
+ src/detection/datetime.c
+ src/detection/disk/disk.c
src/detection/displayserver/displayserver.c
- src/detection/terminalfont/terminalfont.c
+ src/detection/font/font.c
+ src/detection/gpu/gpu.c
+ src/detection/host/host.c
+ src/detection/locale/locale.c
src/detection/media/media.c
+ src/detection/os/os.c
+ src/detection/packages/packages.c
+ src/detection/terminalfont/terminalfont.c
+ src/detection/terminalshell/terminalshell.c
+ src/detection/title.c
+ src/detection/vulkan.c
+ src/logo/builtin.c
+ src/logo/image/im6.c
+ src/logo/image/im7.c
+ src/logo/image/image.c
+ src/logo/logo.c
+ src/modules/battery.c
+ src/modules/bios.c
+ src/modules/board.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/chassis.c
+ src/modules/colors.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/poweradapter.c
- src/modules/locale.c
- src/modules/vulkan.c
- src/modules/localip.c
- src/modules/publicip.c
- src/modules/weather.c
- src/modules/player.c
- src/modules/song.c
- src/modules/datetime.c
+ src/modules/cursor.c
+ src/modules/custom.c
src/modules/date.c
- src/modules/time.c
- src/modules/colors.c
- src/modules/opengl.c
+ src/modules/datetime.c
+ src/modules/de.c
+ src/modules/disk.c
+ src/modules/font.c
+ src/modules/gpu.c
+ src/modules/host.c
+ src/modules/icons.c
+ src/modules/kernel.c
+ src/modules/locale.c
+ src/modules/localip.c
+ src/modules/memory.c
src/modules/opencl.c
+ src/modules/opengl.c
+ src/modules/os.c
+ src/modules/packages.c
+ src/modules/player.c
+ src/modules/poweradapter.c
+ src/modules/processes.c
+ src/modules/publicip.c
+ src/modules/resolution.c
+ src/modules/separator.c
+ src/modules/shell.c
+ src/modules/song.c
+ src/modules/terminal.c
+ src/modules/terminalfont.c
+ src/modules/theme.c
+ src/modules/time.c
+ src/modules/title.c
+ src/modules/uptime.c
src/modules/users.c
+ src/modules/vulkan.c
+ src/modules/weather.c
+ src/modules/wifi.c
+ src/modules/wm.c
+ src/modules/wmtheme.c
+ src/util/FFlist.c
+ src/util/FFstrbuf.c
+ src/util/FFvaluestore.c
)
-if(BSD OR APPLE)
+if(LINUX)
list(APPEND LIBFASTFETCH_SRC
- src/common/sysctl.c
- )
-endif()
-
-if(LINUX OR ANDROID)
- list(APPEND LIBFASTFETCH_SRC
- src/detection/cpu/cpu_linux.c
- src/detection/memory/memory_linux.c
- )
-endif()
-
-if(LINUX OR ANDROID OR BSD)
- list(APPEND LIBFASTFETCH_SRC
- src/detection/cpuUsage/cpuUsage_linux.c
- src/detection/disk/disk_linux.c
- src/detection/poweradapter/poweradapter_linux.c
- src/detection/temps/temps_linux.c
- )
-endif()
-
-if(LINUX OR BSD)
- list(APPEND LIBFASTFETCH_SRC
- src/detection/host/host_linux.c
- src/detection/os/os_linux.c
- src/detection/gpu/gpu_linux.c
+ src/common/networking_linux.c
+ src/common/processing_linux.c
src/detection/battery/battery_linux.c
+ src/detection/bios/bios_linux.c
+ src/detection/board/board_linux.c
+ src/detection/chassis/chassis_linux.c
+ src/detection/cpu/cpu_linux.c
+ src/detection/cpuUsage/cpuUsage_linux.c
+ src/detection/cursor/cursor_linux.c
+ src/detection/disk/disk_linux.c
src/detection/displayserver/linux/displayserver_linux.c
src/detection/displayserver/linux/wayland.c
+ src/detection/displayserver/linux/wmde.c
src/detection/displayserver/linux/xcb.c
src/detection/displayserver/linux/xlib.c
- src/detection/displayserver/linux/wmde.c
- src/detection/terminalfont/terminalfont_linux.c
- src/detection/media/media_linux.c
- src/detection/wmtheme/wmtheme_linux.c
src/detection/font/font_linux.c
+ src/detection/gpu/gpu_linux.c
+ src/detection/gtk.c
+ src/detection/host/host_linux.c
+ src/detection/localip/localip_linux.c
+ src/detection/media/media_linux.c
+ src/detection/memory/memory_linux.c
+ src/detection/opengl/opengl_linux.c
+ src/detection/os/os_linux.c
+ src/detection/packages/packages_linux.c
+ src/detection/poweradapter/poweradapter_nosupport.c
+ src/detection/processes/processes_linux.c
+ src/detection/qt.c
+ src/detection/swap/swap_linux.c
+ src/detection/temps/temps_linux.c
+ src/detection/terminalfont/terminalfont_linux.c
+ src/detection/terminalshell/terminalshell_linux.c
+ src/detection/uptime/uptime_linux.c
+ src/detection/users/users_linux.c
+ src/detection/wifi/wifi_linux.c
+ src/detection/wmtheme/wmtheme_linux.c
)
-endif()
-
-if(APPLE)
+elseif(ANDROID)
list(APPEND LIBFASTFETCH_SRC
+ src/common/networking_linux.c
+ src/common/processing_linux.c
+ src/detection/battery/battery_nosupport.c
+ src/detection/bios/bios_nosupport.c
+ src/detection/board/board_nosupport.c
+ src/detection/chassis/chassis_nosupport.c
+ src/detection/cpu/cpu_linux.c
+ src/detection/cursor/cursor_nosupport.c
+ src/detection/cpuUsage/cpuUsage_linux.c
+ src/detection/disk/disk_linux.c
+ src/detection/displayserver/displayserver_nosupport.c
+ src/detection/font/font_nosupport.c
+ src/detection/gpu/gpu_nosupport.c
+ src/detection/host/host_android.c
+ src/detection/localip/localip_linux.c
+ src/detection/media/media_nosupport.c
+ src/detection/memory/memory_linux.c
+ src/detection/opengl/opengl_linux.c
+ src/detection/os/os_android.c
+ src/detection/packages/packages_linux.c
+ src/detection/poweradapter/poweradapter_nosupport.c
+ src/detection/processes/processes_linux.c
+ src/detection/swap/swap_linux.c
+ src/detection/temps/temps_linux.c
+ src/detection/terminalfont/terminalfont_android.c
+ src/detection/terminalshell/terminalshell_linux.c
+ src/detection/uptime/uptime_linux.c
+ src/detection/users/users_linux.c
+ src/detection/wifi/wifi_nosupport.c
+ src/detection/wmtheme/wmtheme_nosupport.c
+ )
+elseif(BSD)
+ list(APPEND LIBFASTFETCH_SRC
+ src/common/networking_linux.c
+ src/common/processing_linux.c
+ src/common/sysctl.c
+ src/detection/battery/battery_nosupport.c
+ src/detection/bios/bios_nosupport.c
+ src/detection/board/board_nosupport.c
+ src/detection/chassis/chassis_nosupport.c
+ src/detection/cpu/cpu_bsd.c
+ src/detection/cpuUsage/cpuUsage_bsd.c
+ src/detection/cursor/cursor_linux.c
+ src/detection/disk/disk_bsd.c
+ src/detection/displayserver/linux/displayserver_linux.c
+ src/detection/displayserver/linux/wayland.c
+ src/detection/displayserver/linux/wmde.c
+ src/detection/displayserver/linux/xcb.c
+ src/detection/displayserver/linux/xlib.c
+ src/detection/font/font_linux.c
+ src/detection/gpu/gpu_linux.c
+ src/detection/gtk.c
+ src/detection/host/host_bsd.c
+ src/detection/localip/localip_linux.c
+ src/detection/media/media_linux.c
+ src/detection/memory/memory_bsd.c
+ src/detection/opengl/opengl_linux.c
+ src/detection/os/os_linux.c
+ src/detection/packages/packages_linux.c
+ src/detection/poweradapter/poweradapter_nosupport.c
+ src/detection/processes/processes_bsd.c
+ src/detection/qt.c
+ src/detection/swap/swap_bsd.c
+ src/detection/temps/temps_linux.c
+ src/detection/terminalfont/terminalfont_linux.c
+ src/detection/terminalshell/terminalshell_linux.c
+ src/detection/uptime/uptime_bsd.c
+ src/detection/users/users_linux.c
+ src/detection/wifi/wifi_nosupport.c
+ src/detection/wmtheme/wmtheme_linux.c
+ )
+elseif(APPLE)
+ list(APPEND LIBFASTFETCH_SRC
+ src/common/networking_linux.c
+ src/common/processing_linux.c
+ src/common/sysctl.c
+ src/detection/battery/battery_apple.c
+ src/detection/bios/bios_nosupport.c
+ src/detection/board/board_nosupport.c
+ src/detection/chassis/chassis_nosupport.c
+ src/detection/cpu/cpu_apple.c
src/detection/cpuUsage/cpuUsage_apple.c
+ src/detection/cursor/cursor_nosupport.c
+ src/detection/disk/disk_apple.m
+ src/detection/disk/disk_bsd.c
+ src/detection/displayserver/displayserver_apple.c
+ src/detection/font/font_apple.m
+ src/detection/gpu/gpu_apple.c
+ src/detection/host/host_apple.c
+ src/detection/localip/localip_linux.c
+ src/detection/media/media_apple.m
+ src/detection/memory/memory_apple.c
+ src/detection/opengl/opengl_apple.c
+ src/detection/os/os_apple.m
+ src/detection/packages/packages_apple.c
+ src/detection/poweradapter/poweradapter_apple.c
+ src/detection/processes/processes_bsd.c
+ src/detection/swap/swap_apple.c
+ src/detection/temps/temps_apple.c
+ src/detection/terminalfont/terminalfont_apple.m
+ src/detection/terminalshell/terminalshell_linux.c
+ src/detection/uptime/uptime_bsd.c
+ src/detection/users/users_linux.c
+ src/detection/wifi/wifi_apple.m
+ src/detection/wmtheme/wmtheme_apple.m
src/util/apple/cf_helpers.c
src/util/apple/osascript.m
- src/detection/host/host_apple.c
- src/detection/os/os_apple.m
- src/detection/cpu/cpu_apple.c
- src/detection/gpu/gpu_apple.c
- src/detection/battery/battery_apple.c
- src/detection/poweradapter/poweradapter_apple.c
- src/detection/memory/memory_apple.c
- src/detection/displayserver/displayserver_apple.c
- src/detection/terminalfont/terminalfont_apple.m
- src/detection/media/media_apple.m
- src/detection/disk/disk_apple.m
- src/detection/wmtheme/wmtheme_apple.m
- src/detection/temps/temps_apple.c
- src/detection/font/font_apple.m
)
-endif()
-
-if(BSD)
+elseif(WIN32)
list(APPEND LIBFASTFETCH_SRC
- src/detection/cpu/cpu_bsd.c
- src/detection/memory/memory_bsd.c
- )
-endif()
-
-if(ANDROID)
- list(APPEND LIBFASTFETCH_SRC
- src/detection/host/host_android.c
- src/detection/os/os_android.c
- src/detection/gpu/gpu_android.c
- src/detection/battery/battery_android.c
- src/detection/displayserver/displayserver_android.c
- src/detection/terminalfont/terminalfont_android.c
- src/detection/media/media_android.c
- src/detection/wmtheme/wmtheme_android.c
- src/detection/font/font_android.c
+ src/common/networking_windows.c
+ src/common/processing_windows.c
+ src/detection/battery/battery_windows.c
+ src/detection/bios/bios_windows.c
+ src/detection/board/board_windows.c
+ src/detection/chassis/chassis_nosupport.c
+ src/detection/cpu/cpu_windows.c
+ src/detection/cpuUsage/cpuUsage_windows.c
+ src/detection/cursor/cursor_windows.c
+ src/detection/disk/disk_windows.c
+ src/detection/displayserver/displayserver_windows.c
+ src/detection/font/font_windows.c
+ src/detection/gpu/gpu_windows.cpp
+ src/detection/host/host_windows.c
+ src/detection/localip/localip_windows.c
+ src/detection/media/media_nosupport.c
+ src/detection/memory/memory_windows.c
+ src/detection/opengl/opengl_windows.c
+ src/detection/os/os_windows.cpp
+ src/detection/packages/packages_windows.c
+ src/detection/poweradapter/poweradapter_nosupport.c
+ src/detection/processes/processes_windows.cpp
+ src/detection/swap/swap_windows.cpp
+ src/detection/terminalfont/terminalfont_windows.c
+ src/detection/terminalshell/terminalshell_windows.cpp
+ src/detection/uptime/uptime_windows.c
+ src/detection/users/users_windows.c
+ src/detection/wifi/wifi_windows.c
+ src/detection/wmtheme/wmtheme_windows.c
+ src/util/windows/getline.c
+ src/util/windows/pwd.c
+ src/util/windows/registry.c
+ src/util/windows/unicode.c
+ src/util/windows/utsname.c
+ src/util/windows/wmi.cpp
)
endif()
@@ -350,31 +497,38 @@ add_library(libfastfetch OBJECT
target_compile_definitions(libfastfetch PUBLIC _GNU_SOURCE)
-CHECK_INCLUDE_FILE("sys/sysinfo.h" HAVE_SYSINFO_H)
-if(HAVE_SYSINFO_H)
- # needs to be public, because changes fastfech.h ABI
- target_compile_definitions(libfastfetch PUBLIC FF_HAVE_SYSINFO_H)
-endif()
-
CHECK_INCLUDE_FILE("utmpx.h" HAVE_UTMPX_H)
if(HAVE_UTMPX_H)
target_compile_definitions(libfastfetch PRIVATE FF_HAVE_UTMPX_H)
endif()
-function(ff_lib_enable VARNAME)
+function(ff_lib_enable VARNAME PKGCONFIG_NAMES CMAKE_NAME)
if(NOT ENABLE_${VARNAME})
return()
endif()
- pkg_search_module(${VARNAME} ${ARGN})
+ if(PKG_CONFIG_FOUND)
+ pkg_search_module(${VARNAME} QUIET ${PKGCONFIG_NAMES})
+ endif()
+
if(NOT ${VARNAME}_FOUND)
- message(WARNING "Package ${ARGV1} not found, building without support.")
+ find_package(${CMAKE_NAME} QUIET)
+
+ set(${VARNAME}_FOUND ${${CMAKE_NAME}_FOUND})
+ set(${VARNAME}_INCLUDE_DIRS ${${CMAKE_NAME}_INCLUDE_DIRS})
+ set(${VARNAME}_LIBRARIES ${${CMAKE_NAME}_LIBRARIES})
+ set(${VARNAME}_CFLAGS_OTHER ${${CMAKE_NAME}_CFLAGS_OTHER})
+ endif()
+
+ if(NOT ${VARNAME}_FOUND)
+ message(STATUS "Library: missing: ${VARNAME}")
return()
endif()
+ message(STATUS "Library: found ${VARNAME}")
+
target_compile_definitions(libfastfetch PRIVATE FF_HAVE_${VARNAME}=1)
target_include_directories(libfastfetch PRIVATE ${${VARNAME}_INCLUDE_DIRS})
- target_link_directories(libfastfetch PRIVATE ${${VARNAME}_LIBRARY_DIRS})
foreach(FLAG ${${VARNAME}_CFLAGS_OTHER})
if(FLAG MATCHES "-D.*")
@@ -384,40 +538,144 @@ function(ff_lib_enable VARNAME)
endforeach()
endfunction()
-ff_lib_enable(LIBPCI libpci)
-ff_lib_enable(VULKAN vulkan)
-ff_lib_enable(WAYLAND wayland-client)
-ff_lib_enable(XCB_RANDR xcb-randr)
-ff_lib_enable(XCB xcb)
-ff_lib_enable(XRANDR xrandr)
-ff_lib_enable(X11 x11)
-ff_lib_enable(GIO gio-2.0)
-ff_lib_enable(DCONF dconf)
-ff_lib_enable(DBUS dbus-1)
-ff_lib_enable(XFCONF libxfconf-0)
-ff_lib_enable(SQLITE3 sqlite3)
-ff_lib_enable(RPM rpm)
-ff_lib_enable(IMAGEMAGICK7 MagickCore-7.Q16HDRI MagickCore-7.Q16 MagickCore-7 /usr/lib/imagemagick7/pkgconfig/MagickCore-7.Q16HDRI.pc /usr/lib/imagemagick7/pkgconfig/MagickCore-7.Q16.pc /usr/lib/imagemagick7/pkgconfig/MagickCore-7.pc)
-ff_lib_enable(IMAGEMAGICK6 MagickCore-6.Q16HDRI MagickCore-6.Q16 MagickCore-6 /usr/lib/imagemagick6/pkgconfig/MagickCore-6.Q16HDRI.pc /usr/lib/imagemagick6/pkgconfig/MagickCore-6.Q16.pc /usr/lib/imagemagick6/pkgconfig/MagickCore-6.pc)
-ff_lib_enable(ZLIB zlib)
-ff_lib_enable(CHAFA chafa>=1.10)
-ff_lib_enable(EGL egl)
-ff_lib_enable(GLX glx)
-ff_lib_enable(OSMESA osmesa)
-ff_lib_enable(OPENCL OpenCL)
-ff_lib_enable(LIBCJSON libcjson)
-ff_lib_enable(FREETYPE freetype2)
+ff_lib_enable(LIBPCI
+ "libpci"
+ "Pci"
+)
+ff_lib_enable(VULKAN
+ "vulkan"
+ "Vulkan"
+)
+ff_lib_enable(WAYLAND
+ "wayland-client"
+ "WaylandClient"
+)
+ff_lib_enable(XCB_RANDR
+ "xcb-randr"
+ "XcbRandr"
+)
+ff_lib_enable(XCB
+ "xcb"
+ "Xcb"
+)
+ff_lib_enable(XRANDR
+ "xrandr"
+ "XRandr"
+)
+ff_lib_enable(X11
+ "x11"
+ "X11"
+)
+ff_lib_enable(GIO
+ "gio-2.0"
+ "GIO"
+)
+ff_lib_enable(DCONF
+ "dconf"
+ "DConf"
+)
+ff_lib_enable(DBUS
+ "dbus-1"
+ "DBus"
+)
+ff_lib_enable(XFCONF
+ "libxfconf-0"
+ "XFConf"
+)
+ff_lib_enable(SQLITE3
+ "sqlite3"
+ "SQLite3"
+)
+ff_lib_enable(RPM
+ "rpm"
+ "RPM"
+)
+ff_lib_enable(IMAGEMAGICK7
+ "MagickCore-7.Q16HDRI;MagickCore-7.Q16;MagickCore-7;/usr/lib/imagemagick7/pkgconfig/MagickCore-7.Q16HDRI.pc;/usr/lib/imagemagick7/pkgconfig/MagickCore-7.Q16.pc;/usr/lib/imagemagick7/pkgconfig/MagickCore-7.pc"
+ "ImageMagick7"
+)
+ff_lib_enable(IMAGEMAGICK6
+ "MagickCore-6.Q16HDRI;MagickCore-6.Q16;MagickCore-6;/usr/lib/imagemagick6/pkgconfig/MagickCore-6.Q16HDRI.pc;/usr/lib/imagemagick6/pkgconfig/MagickCore-6.Q16.pc;/usr/lib/imagemagick6/pkgconfig/MagickCore-6.pc"
+ "ImageMagick6"
+)
+ff_lib_enable(ZLIB
+ "zlib"
+ "ZLIB"
+)
+ff_lib_enable(CHAFA
+ "chafa>=1.10"
+ "Chafa"
+)
+ff_lib_enable(EGL
+ "egl"
+ "EGL"
+)
+ff_lib_enable(GLX
+ "glx"
+ "GLX"
+)
+ff_lib_enable(OSMESA
+ "osmesa"
+ "OSMesa"
+)
+ff_lib_enable(OPENCL
+ "OpenCL"
+ "OpenCL"
+)
+ff_lib_enable(LIBCJSON
+ "libcjson"
+ "CJson"
+)
+ff_lib_enable(FREETYPE
+ "freetype2"
+ "FreeType2"
+)
+
+if(ENABLE_THREADS)
+ target_compile_definitions(libfastfetch PRIVATE FF_HAVE_THREADS)
+ if(CMAKE_USE_PTHREADS_INIT) #Threads::Threads is not set for WIN32
+ target_link_libraries(libfastfetch PRIVATE Threads::Threads)
+ endif()
+endif()
+
+
+if(ENABLE_BUFFER)
+ target_compile_definitions(libfastfetch PRIVATE FF_ENABLE_BUFFER)
+endif()
if(APPLE)
target_link_libraries(libfastfetch
PRIVATE "-framework CoreFoundation"
PRIVATE "-framework IOKit"
- PRIVATE "-framework CoreGraphics"
PRIVATE "-framework OpenGL"
PRIVATE "-framework OpenCL"
PRIVATE "-framework Cocoa"
+ PRIVATE "-framework CoreWLAN"
PRIVATE "-weak_framework MediaRemote -F /System/Library/PrivateFrameworks"
)
+elseif(WIN32)
+ target_link_libraries(libfastfetch
+ PRIVATE "dwmapi"
+ PRIVATE "gdi32"
+ PRIVATE "iphlpapi"
+ PRIVATE "ole32"
+ PRIVATE "oleaut32"
+ PRIVATE "opengl32"
+ PRIVATE "wbemuuid"
+ PRIVATE "ws2_32"
+ PRIVATE "ntdll"
+ PRIVATE "version"
+ PRIVATE "setupapi"
+ PRIVATE "dxgi"
+ PRIVATE "wtsapi32"
+ PRIVATE "powrprof"
+ )
+ if(USE_WIN_NTAPI)
+ target_compile_definitions(libfastfetch PRIVATE FF_USE_WIN_NTAPI)
+ endif()
+ if(USE_WIN_GPU_DXGI)
+ target_compile_definitions(libfastfetch PRIVATE FF_USE_WIN_GPU_DXGI)
+ endif()
endif()
target_include_directories(libfastfetch
@@ -427,7 +685,6 @@ target_include_directories(libfastfetch
target_link_libraries(libfastfetch
PRIVATE ${CMAKE_DL_LIBS}
- PRIVATE Threads::Threads
)
######################
@@ -437,6 +694,9 @@ target_link_libraries(libfastfetch
add_executable(fastfetch
src/fastfetch.c
)
+target_compile_definitions(fastfetch
+ PRIVATE FASTFETCH_TARGET_BINARY_NAME=fastfetch
+)
target_link_libraries(fastfetch
PRIVATE libfastfetch
)
@@ -444,10 +704,25 @@ target_link_libraries(fastfetch
add_executable(flashfetch
src/flashfetch.c
)
+target_compile_definitions(flashfetch
+ PRIVATE FASTFETCH_TARGET_BINARY_NAME=flashfetch
+)
target_link_libraries(flashfetch
PRIVATE libfastfetch
)
+if(WIN32)
+ set(TARGET_NAME fastfetch)
+ target_sources(fastfetch
+ PRIVATE src/util/windows/version.rc
+ )
+
+ set(TARGET_NAME flashfetch)
+ target_sources(flashfetch
+ PRIVATE src/util/windows/version.rc
+ )
+endif()
+
###################
# Testing targets #
###################
@@ -519,12 +794,6 @@ if(LINUX)
set(CPACK_DEBIAN_PACKAGE_DEPENDS "libc6")
set(CPACK_RPM_PACKAGE_LICENSE "MIT")
-elseif(APPLE)
- set(CPACK_GENERATOR "${CPACK_GENERATOR};DragNDrop")
-
- set(CPACK_DMG_DISABLE_APPLICATIONS_SYMLINK TRUE)
-elseif(BSD)
- set(CPACK_GENERATOR "${CPACK_GENERATOR};FreeBSD")
endif()
set(CPACK_SET_DESTDIR ON)
diff --git a/README.md b/README.md
index 8eb20f122..9d17386d1 100644
--- a/README.md
+++ b/README.md
@@ -1,6 +1,6 @@
# Fastfetch
-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 pure c, with performance and customizability in mind. Currently Linux, Android, BSD and MacOS are supported.
+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 pure c, with performance and customizability in mind. Currently, Linux, Android, FreeBSD, MacOS and Windows 7+ are supported.
@@ -21,9 +21,13 @@ There are some premade config files in [`presets`](presets), including the ones
## Dependencies
-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.
+Fastfetch dynamically loads needed libraries if they are available. On Linux, its only hard dependencies are `libc` (any implementation of the c standard library), `libdl` and [`libpthread`](https://man7.org/linux/man-pages/man7/pthreads.7.html) (if built with multithreading support). They are all shipped with [`glibc`](https://www.gnu.org/software/libc/), which is already installed on most linux distributions.
+
The following libraries are used if present at runtime:
+
+### Linux and FreeBSD
+
* [`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/),
@@ -45,34 +49,61 @@ The following libraries are used if present at runtime:
* [`libsqlite3`](https://www.sqlite.org/index.html): Needed for pkg & rpm package count.
* [`librpm`](http://rpm.org/): Slower fallback for rpm package count. Needed on openSUSE.
* [`libcJSON`](https://github.com/DaveGamble/cJSON): Needed for Windows Terminal font ( WSL ).
-* [`freetype`](https://www.freetype.org/): Needed for Termux font detection ( Android ).
+
+### macOS
+
+* [`MediaRemote`](https://iphonedev.wiki/index.php/MediaRemote.framework): Need for Media detection. It's a private framework provided by newer macOS system.
+* [`MoltenVK`](https://github.com/KhronosGroup/MoltenVK): Vulkan driver for macOS. [`molten-vk`](https://github.com/Homebrew/homebrew-core/blob/HEAD/Formula/molten-vk.rb)
+* [`libmagickcore` (ImageMagick)](https://www.imagemagick.org/): Images in terminal using sixel graphics protocol. [`imagemagick`](https://github.com/Homebrew/homebrew-core/blob/HEAD/Formula/imagemagick.rb)
+* [`libchafa`](https://github.com/hpjansson/chafa): Image output as ascii art. [`chafa`](https://github.com/Homebrew/homebrew-core/blob/HEAD/Formula/chafa.rb)
+
+For the image logo, iTerm with iterm image protocol should work. Apple Terminal is not supported.
+
+### Windows
+
+* [`libcJSON`](https://github.com/DaveGamble/cJSON): Used for Windows Terminal font detection.
+* [`wlanapi`](https://learn.microsoft.com/en-us/windows/win32/api/wlanapi/): A system dll which isn't supported by Windows Server by default. Used for Wifi info detection.
+* [`libvulkan`](https://www.vulkan.org/): Vulkan module. Usually has been provided by GPU drivers.
+* [`libOpenCL`](https://www.khronos.org/opencl/): OpenCL module
+
+Note: In Windows 7, 8 and 8.1, [ConEmu](https://conemu.github.io/en/AnsiEscapeCodes.html) is required to run fastfetch due to [the lack of ASCII escape code native support](https://en.wikipedia.org/wiki/ANSI_escape_code#DOS,_OS/2,_and_Windows). In addition, special build `fastfetch-windows-old` in [Github Actions](https://github.com/LinusDierheimer/fastfetch/actions) is provided to support these old systems, which
+
+1. Build with the ancient MSVCRT C runtime library, instead of the modern [UCRT](https://learn.microsoft.com/en-us/cpp/windows/universal-crt-deployment) C runtime library
+2. Disable stdout application buffer, which seems to problematic for ConEmu.
+
+For the image logo, only chafa is supported due to [the design flaw of ConPTY](https://github.com/microsoft/terminal/issues/1173). In addition, chafa support is not built by default due to the massive dependencies of imagemagick. You must built it yourself.
+
+### Android
+
+* [`freetype`](https://www.freetype.org/): Used for Termux font detection.
+* [`libvulkan`](https://www.vulkan.org/): Vulkan module, also used for GPU detection. Usually has been provided by Android system.
## Support status
All categories not listed here should work without needing a specific implementation.
##### 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, Swap, Disk, Battery, Power Adapter, Player, Media, Vulkan, OpenGL, OpenCL, LocalIP, PublicIP, DateTime, Date, Time, Locale, Colors, Break, Custom
+Title, Separator, OS, Host, Bios, Board, Kernel, Uptime, Processes, Packages, Shell, Resolution, DE, WM, WMTheme, Theme, Icons, Font, Cursor, Terminal, Terminal Font, CPU, CPUUsage, GPU, Memory, Swap, Disk, Battery, Power Adapter, Player, Media, Vulkan, OpenGL, OpenCL, LocalIP, PublicIP, Wifi, DateTime, Date, Time, Locale, Colors, Break, Custom
```
##### Logos
```
-AlmaLinux, Alpine, Android, Arch, Arco, Artix, Bedrock, CachyOS, CentOS, Crystal, Debian, Devuan, Deepin, Endeavour, Fedora, FreeBSD, Garuda, Gentoo, KDE Neon, KISS, Kubuntu, LangitKetujuh, Linux, MacOS, Manjaro, Mint, MSYS2, NixOS, OpenSUSE, OpenSUSE Tumbleweed, OpenSUSE LEAP, Pop!_OS, RebornOS, RedstarOS, Rocky, Rosa, Slackware, Ubuntu, Void, Zorin
+AlmaLinux, Alpine, Android, Arch, Arco, Artix, Bedrock, CachyOS, CentOS, CRUX, Crystal, Debian, Devuan, Deepin, Endeavour, Enso, Fedora, FreeBSD, Garuda, Gentoo, KDE Neon, KISS, Kubuntu, LangitKetujuh, Linux, MacOS, Manjaro, Mint, MSYS2, NixOS, Nobara, OpenSUSE, OpenSUSE Tumbleweed, OpenSUSE LEAP, Parabola, Pop!_OS, RebornOS, RedstarOS, Rocky, Rosa, Slackware, Solus, Ubuntu, Vanilla, Void, Windows 11, Windows 8, Windows, 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.
* To disable the logo, use `--logo none`.
* Get a list of all available logos with `fastfetch --print-logos`.
-* Printing images as logo is supported using Sixel / Kitty graphics protocol or chafas image to text conversion.
+* Printing images as logo is supported using Sixel / Kitty / iTerm graphics protocol or chafas image to text conversion.
##### Package managers
```
-Pacman, dpkg, rpm, emerge, xbps, nix, Flatpak, Snap, apk, pkg, brew, MacPorts
+Pacman, dpkg, rpm, emerge, eopkg, xbps, nix, Flatpak, Snap, apk, pkg, brew, MacPorts, scoop, Chocolatey
```
##### WM themes
```
-KWin, Mutter, Muffin, Marco, XFWM, Openbox (LXDE, LXQT & without DE), Quartz Compositor (macOS)
+KWin, Mutter, Muffin, Marco, XFWM, Openbox (LXDE, LXQT & without DE), Quartz Compositor (macOS), DWM (Windows)
```
##### DE versions
@@ -82,12 +113,12 @@ KDE Plasma, Gnome, Cinnamon, Mate, XFCE4, LXQt
##### Terminal fonts
```
-Konsole, Gnome Terminal, Tilix, XFCE4 Terminal, Alacritty, LXTerminal, iTerm2, Apple Terminal, TTY, Windows Terminal, Termux
+Konsole, Gnome Terminal, Tilix, XFCE4 Terminal, Alacritty, Kitty, LXTerminal, Deepin Terminal, iTerm2, Apple Terminal, Warp, TTY, Windows Terminal, Termux, mintty, ConEmu
```
## 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:
+fastfetch uses [`cmake`](https://cmake.org/) for building. [`pkg-config`](https://www.freedesktop.org/wiki/Software/pkg-config/) is recommended for better library detection. The simplest steps to build the fastfetch and flashfetch binaries are:
```bash
mkdir -p build
cd build
@@ -95,7 +126,11 @@ 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.
+If the build process 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.
+
+### Building on Windows
+
+Currently GCC or clang is required (MSVC is not supported). MSYS2 with CLANG64 sub system is suggested (and tested) to build fastfetch.
## Packaging
@@ -111,7 +146,7 @@ If pkg-config fails to find the headers for a library listed in [dependencies](#
## 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.
+> 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
+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
diff --git a/completions/bash b/completions/bash
index a27172dfd..10356ce85 100644
--- a/completions/bash
+++ b/completions/bash
@@ -169,11 +169,10 @@ __fastfetch_completion()
local FF_OPTIONS_BOOL=(
"-r"
- "--recache"
- "--nocache"
"--show-errors"
"--logo-print-remaining"
"--multithreading"
+ "--stat"
"--allow-slow-operations"
"--disable-linewrap"
"--hide-cursor"
@@ -188,6 +187,8 @@ __fastfetch_completion()
"--pipe"
"--title-fqdn"
"--escape-bedrock"
+ "--disk-show-removable"
+ "--disk-show-hidden"
)
local FF_OPTIONS_STRING=(
@@ -216,6 +217,7 @@ __fastfetch_completion()
"--set"
"--set-keyless"
"--player-name"
+ "--percent-type"
"--public-ip-url"
"--public-ip-timeout"
"--weather-output-format"
@@ -304,6 +306,9 @@ __fastfetch_completion()
"--public-ip-key"
"--public-ip-format"
"--public-ip-error"
+ "--wifi-key"
+ "--wifi-format"
+ "--wifi-error"
"--weather-key"
"--weather-format"
"--weather-error"
diff --git a/presets/all b/presets/all
index a1706f3b1..d28a033a4 100644
--- a/presets/all
+++ b/presets/all
@@ -1 +1 @@
---structure Title:Separator:OS:Host:Kernel:Uptime:Processes:Packages:Shell:Resolution:DE:WM:WMTheme:Theme:Icons:Font:Cursor:Terminal:TerminalFont:CPU:GPU:Memory:Swap:Disk:Battery:PowerAdapter:Player:Song:PublicIP:LocalIP:DateTime:Locale:Vulkan:OpenGL:OpenCL:Users:Weather:Break:Colors
+--structure Title:Separator:OS:Host:Bios:Board:Chassis:Kernel:Uptime:Processes:Packages:Shell:Resolution:DE:WM:WMTheme:Theme:Icons:Font:Cursor:Terminal:TerminalFont:CPU:CPUUsage:GPU:Memory:Swap:Disk:Battery:PowerAdapter:Player:Song:PublicIP:LocalIP:Wifi:DateTime:Locale:Vulkan:OpenGL:OpenCL:Users:Weather:Break:Colors
diff --git a/presets/devinfo b/presets/devinfo
index 57859baad..2ddf030b5 100644
--- a/presets/devinfo
+++ b/presets/devinfo
@@ -1,4 +1,3 @@
--disable-linewrap false
--multithreading false
--show-errors
---recache
diff --git a/presets/verbose b/presets/verbose
index 9b7713eae..5a9f2f0e5 100644
--- a/presets/verbose
+++ b/presets/verbose
@@ -1,9 +1,9 @@
--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: {}
+--host-format Family: product_family: {}; product_name: {}; product_version: {}; product_sku: {}; 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: {}; apk: {}; flatpak: {}; snap: {}; brew: {}
+--packages-format All: {}; pacman: {}; pacman branch: {}; dpkg: {}; rpm: {}; emerge: {}; eopkg: {}; xbps: {}; apk: {}; flatpak: {}; snap: {}; brew: {}; brew-cask: {}; port: {}; scoop: {}; choco: {}
--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: {}
@@ -19,7 +19,7 @@
--cpu-usage-format Percentage: {}
--gpu-format Vendor: {}; Name: {}; Driver: {}; Temperature: {}; CoreCount: {}
--memory-format Used: {}; Total: {}; Percentage: {}
---disk-format Used: {}; Total: {}; Files: {}; Percentage: {}
+--disk-format SizeUsed: {}; SizeTotal: {}; SizePercentage: {}; FilesUsed: {}; FilesTotal: {}; FilesPercentage: {}; Removable: {}; Hidden: {}; Filesystem: {}; Name: {}
--battery-format Manufactor: {}; Model: {}; Technology: {}; Capacity: {}; Status: {}
--poweradapter-format Watts: {}; Name: {}; Manufactor: {}; Model: {}; Description: {}
--player-format Pretty: {}; Name: {}; Bus: {}; Url: {}
diff --git a/src/common/bar.c b/src/common/bar.c
new file mode 100644
index 000000000..34b14aa7a
--- /dev/null
+++ b/src/common/bar.c
@@ -0,0 +1,49 @@
+#include "util/textModifier.h"
+#include "bar.h"
+
+// green, yellow, red: print the color on nth (0~9) block
+// set its value == 10 means the color will not be printed
+void ffAppendPercentBar(FFinstance* instance, FFstrbuf* buffer, uint8_t percent, uint8_t green, uint8_t yellow, uint8_t red)
+{
+ assert(green <= 10 && yellow <= 10 && red <= 10);
+
+ // [ 0%, 5%) prints 0 blocks
+ // [ 5%, 15%) prints 1 block;
+ // ...
+ // [85%, 95%) prints 9 blocks;
+ // [95%,100%] prints 10 blocks
+ percent = (uint8_t)(percent + 5) / 10;
+ assert(percent <= 10);
+
+ if(!instance->config.pipe)
+ ffStrbufAppendS(buffer, "\033[97m[ ");
+ else
+ ffStrbufAppendS(buffer, "[ ");
+
+ for (uint8_t i = 0; i < percent; ++i)
+ {
+ if(!instance->config.pipe)
+ {
+ if (i == green)
+ ffStrbufAppendS(buffer, "\033[32m");
+ else if (i == yellow)
+ ffStrbufAppendS(buffer, "\033[93m");
+ else if (i == red)
+ ffStrbufAppendS(buffer, "\033[91m");
+ }
+ ffStrbufAppendS(buffer, "■");
+ }
+
+ if (percent < 10)
+ {
+ if(!instance->config.pipe)
+ ffStrbufAppendS(buffer, "\033[97m");
+ for (uint8_t i = percent; i < 10; ++i)
+ ffStrbufAppendS(buffer, "-");
+ }
+
+ if(!instance->config.pipe)
+ ffStrbufAppendS(buffer, "\033[97m ]" FASTFETCH_TEXT_MODIFIER_RESET);
+ else
+ ffStrbufAppendS(buffer, " ]");
+}
diff --git a/src/common/bar.h b/src/common/bar.h
new file mode 100644
index 000000000..a6c2be234
--- /dev/null
+++ b/src/common/bar.h
@@ -0,0 +1,16 @@
+#pragma once
+
+#ifndef FF_INCLUDED_common_bar
+#define FF_INCLUDED_common_bar
+
+#include "fastfetch.h"
+
+enum
+{
+ FF_PERCENTAGE_TYPE_NUM_BIT = 1,
+ FF_PERCENTAGE_TYPE_BAR_BIT = 2,
+};
+
+void ffAppendPercentBar(FFinstance* instance, FFstrbuf* buffer, uint8_t percent, uint8_t green, uint8_t yellow, uint8_t red);
+
+#endif
diff --git a/src/common/caching.c b/src/common/caching.c
deleted file mode 100644
index 12a34c82b..000000000
--- a/src/common/caching.c
+++ /dev/null
@@ -1,324 +0,0 @@
-#include "fastfetch.h"
-#include "common/caching.h"
-#include "common/io.h"
-#include "common/printing.h"
-
-#include
-#include
-
-#define FF_CACHE_VERSION_NAME "cacheversion"
-#define FF_CACHE_VERSION_EXTENSION "ffv"
-
-#define FF_CACHE_VALUE_EXTENSION "ffcv"
-#define FF_CACHE_SPLIT_EXTENSION "ffcs"
-
-#define FF_CACHE_EXTENSION_V1 "ffc1"
-
-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, FF_CACHE_VERSION_NAME, FF_CACHE_VERSION_EXTENSION, &content);
-
- const char exactVersion[] = FASTFETCH_PROJECT_VERSION FASTFETCH_PROJECT_VERSION_TWEAK;
-
- bool isSameVersion = ffStrbufCompS(&content, exactVersion) == 0;
- ffStrbufDestroy(&content);
- if(isSameVersion)
- return;
-
- instance->config.recache = true;
-
- FFstrbuf version;
- ffStrbufInitA(&version, sizeof(exactVersion));
- ffStrbufAppendS(&version, exactVersion);
- writeCacheFile(instance, FF_CACHE_VERSION_NAME, FF_CACHE_VERSION_EXTENSION, &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);
-}
-
-typedef struct FFCacheRead
-{
- FFstrbuf data;
- uint32_t position;
-} FFCacheRead;
-
-static bool cacheReadStrbuf(FFCacheRead* cacheRead, FFstrbuf* strbuf)
-{
- if(cacheRead->position >= cacheRead->data.length)
- return false;
-
- ffStrbufAppendS(strbuf, cacheRead->data.chars + cacheRead->position);
- cacheRead->position += strbuf->length + 1; // skip the null byte too
- return true;
-}
-
-static bool cacheReadData(FFCacheRead* cacheRead, size_t dataSize, void* data)
-{
- if(cacheRead->position + dataSize > cacheRead->data.length)
- return false;
-
- memcpy(data, cacheRead->data.chars + cacheRead->position, dataSize);
- cacheRead->position += (uint32_t) dataSize;
- return true;
-}
-
-static bool cacheResetStrbuf(FFCache* cache, FFstrbuf* strbuf)
-{
- FF_UNUSED(cache);
- ffStrbufClear(strbuf);
- return true;
-}
-
-static bool cacheResetData(FFCache* cacheRead, size_t dataSize, void* data)
-{
- FF_UNUSED(cacheRead, dataSize, data);
- return true;
-}
-
-bool ffCacheRead(const FFinstance* instance, void* obj, const char* cacheName, FFCacheMethodCallback callback)
-{
- if(instance->config.recache)
- return false;
-
- FFCacheRead cache;
- bool result;
-
- FFstrbuf path;
- ffStrbufInitA(&path, 128);
- getCacheFilePath(instance, cacheName, FF_CACHE_EXTENSION_V1, &path);
-
- cache.position = 0;
-
- ffStrbufInitA(&cache.data, 256);
- result = ffAppendFileBuffer(path.chars, &cache.data);
-
- if(result)
- result = callback(obj, &cache, (FFCacheMethodStrbuf) cacheReadStrbuf, (FFCacheMethodData) cacheReadData);
-
- if(result)
- result = cache.position == cache.data.length;
-
- if(!result)
- callback(obj, NULL, (FFCacheMethodStrbuf) cacheResetStrbuf, (FFCacheMethodData) cacheResetData);
-
- ffStrbufDestroy(&cache.data);
- ffStrbufDestroy(&path);
-
- return result;
-}
-
-typedef struct FFCacheWrite
-{
- FFstrbuf data;
-} FFCacheWrite;
-
-static bool cacheWriteStrbuf(FFCacheWrite* cacheWrite, FFstrbuf* strbuf)
-{
- ffStrbufEnsureFree(&cacheWrite->data, strbuf->length);
- memcpy(cacheWrite->data.chars + cacheWrite->data.length, strbuf->chars, strbuf->length +1); //Copy the nullbyte too
- cacheWrite->data.length += strbuf->length + 1;
- return true;
-}
-
-static bool cacheWriteData(FFCacheWrite* cacheWrite, size_t dataSize, const void* data)
-{
- ffStrbufEnsureFree(&cacheWrite->data, (uint32_t) dataSize);
- memcpy(cacheWrite->data.chars + cacheWrite->data.length, data, dataSize);
- cacheWrite->data.length += (uint32_t) dataSize;
- return true;
-}
-
-void ffCacheWrite(const FFinstance* instance, void* obj, const char* cacheName, FFCacheMethodCallback callback)
-{
- if(!instance->config.cacheSave)
- return;
-
- FFCacheWrite cache;
-
- FFstrbuf path;
- ffStrbufInitA(&path, 128);
- getCacheFilePath(instance, cacheName, FF_CACHE_EXTENSION_V1, &path);
-
- ffStrbufInitA(&cache.data, 256);
- callback(obj, &cache, (FFCacheMethodStrbuf) cacheWriteStrbuf, (FFCacheMethodData) cacheWriteData);
- ffWriteFileBuffer(path.chars, &cache.data);
-
- ffStrbufDestroy(&cache.data);
- ffStrbufDestroy(&path);
-}
diff --git a/src/common/caching.h b/src/common/caching.h
deleted file mode 100644
index d8e17d473..000000000
--- a/src/common/caching.h
+++ /dev/null
@@ -1,33 +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);
-
-typedef void FFCache;
-
-typedef bool(*FFCacheMethodStrbuf)(FFCache* cache, FFstrbuf* strbuf);
-typedef bool(*FFCacheMethodData)(FFCache* cache, size_t dataSize, void* data);
-typedef bool(*FFCacheMethodCallback)(void* data, FFCache* cache, FFCacheMethodStrbuf strbufMethod, FFCacheMethodData dataMethod);
-
-bool ffCacheRead(const FFinstance* instance, void* obj, const char* cacheName, FFCacheMethodCallback callback);
-void ffCacheWrite(const FFinstance* instance, void* obj, const char* cacheName, FFCacheMethodCallback callback);
-
-#endif
diff --git a/src/common/format.c b/src/common/format.c
index 688ae3887..b491713cf 100644
--- a/src/common/format.c
+++ b/src/common/format.c
@@ -1,23 +1,28 @@
#include "fastfetch.h"
#include "common/format.h"
#include "common/parsing.h"
+#include "util/textModifier.h"
+
+#include
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);
+ ffStrbufAppendF(buffer, "%" PRIu32, *(uint32_t*)formatarg->value);
else if(formatarg->type == FF_FORMAT_ARG_TYPE_UINT16)
- ffStrbufAppendF(buffer, "%hu", *(uint16_t*)formatarg->value);
+ ffStrbufAppendF(buffer, "%" PRIu16, *(uint16_t*)formatarg->value);
else if(formatarg->type == FF_FORMAT_ARG_TYPE_UINT8)
- ffStrbufAppendF(buffer, "%hhu", *(uint8_t*)formatarg->value);
+ ffStrbufAppendF(buffer, "%" PRIu8, *(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_BOOL)
+ ffStrbufAppendS(buffer, formatarg->value != NULL ? "true" : "false");
else if(formatarg->type == FF_FORMAT_ARG_TYPE_LIST)
{
const FFlist* list = formatarg->value;
@@ -34,6 +39,14 @@ void ffFormatAppendFormatArg(FFstrbuf* buffer, const FFformatarg* formatarg)
}
}
+/**
+ * @brief parses a string to a uint32_t
+ *
+ * If the string can't be parsed, or is < 1, uint32_t max is returned.
+ *
+ * @param placeholderValue the string to parse
+ * @return uint32_t the parsed value
+ */
static inline uint32_t getArgumentIndex(const FFstrbuf* placeholderValue)
{
uint32_t result = UINT32_MAX;
@@ -41,7 +54,7 @@ static inline uint32_t getArgumentIndex(const FFstrbuf* placeholderValue)
if(placeholderValue->chars[0] != '-')
sscanf(placeholderValue->chars, "%u", &result);
- return result;
+ return result == 0 ? UINT32_MAX : result;
}
static inline void appendInvalidPlaceholder(FFstrbuf* buffer, const char* start, const FFstrbuf* placeholderValue, uint32_t index, uint32_t formatStringLength)
@@ -72,7 +85,8 @@ static inline bool formatArgSet(const FFformatarg* arg)
(arg->type == FF_FORMAT_ARG_TYPE_STRING && ffStrSet(arg->value)) ||
(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)
+ (arg->type == FF_FORMAT_ARG_TYPE_UINT && *(uint32_t*)arg->value > 0) ||
+ (arg->type == FF_FORMAT_ARG_TYPE_BOOL && arg->value != NULL)
);
}
diff --git a/src/common/format.h b/src/common/format.h
index 51413a1fb..d860dbd29 100644
--- a/src/common/format.h
+++ b/src/common/format.h
@@ -13,7 +13,8 @@ typedef enum FFformatargtype
FF_FORMAT_ARG_TYPE_STRING,
FF_FORMAT_ARG_TYPE_STRBUF,
FF_FORMAT_ARG_TYPE_DOUBLE,
- FF_FORMAT_ARG_TYPE_LIST
+ FF_FORMAT_ARG_TYPE_LIST,
+ FF_FORMAT_ARG_TYPE_BOOL
} FFformatargtype;
typedef struct FFformatarg
@@ -25,4 +26,6 @@ typedef struct FFformatarg
void ffFormatAppendFormatArg(FFstrbuf* buffer, const FFformatarg* formatarg);
void ffParseFormatString(FFstrbuf* buffer, const FFstrbuf* formatstr, uint32_t numArgs, const FFformatarg* arguments);
+#define FF_FORMAT_ARG_VALUE_BOOL(xpr) ((xpr) ? (const void*) 1 : NULL)
+
#endif
diff --git a/src/common/init.c b/src/common/init.c
index a9eb9b25a..d36c6ef5c 100644
--- a/src/common/init.c
+++ b/src/common/init.c
@@ -1,25 +1,27 @@
#include "fastfetch.h"
-#include "common/caching.h"
#include "common/parsing.h"
+#include "common/thread.h"
#include "detection/qt.h"
#include "detection/gtk.h"
#include "detection/displayserver/displayserver.h"
+#include "util/textModifier.h"
#include
#include
#include
-#include
-#include
-
-static bool strbufEqualsAdapter(const void* first, const void* second)
-{
- return ffStrbufComp(second, first) == 0;
-}
+#ifdef _WIN32
+ #include
+ #include
+#else
+ #include
+#endif
static void initConfigDirs(FFstate* state)
{
ffListInit(&state->configDirs, sizeof(FFstrbuf));
+ #if !(defined(_WIN32) || defined(__APPLE__) || defined(__ANDROID__))
+
const char* xdgConfigHome = getenv("XDG_CONFIG_HOME");
if(ffStrSet(xdgConfigHome))
{
@@ -29,8 +31,10 @@ static void initConfigDirs(FFstate* state)
ffStrbufEnsureEndsWithC(buffer, '/');
}
+ #endif
+
#define FF_ENSURE_ONLY_ONCE_IN_LIST(element) \
- if(ffListFirstIndexComp(&state->configDirs, element, strbufEqualsAdapter) < state->configDirs.length - 1) \
+ if(ffListFirstIndexComp(&state->configDirs, element, (bool(*)(const void*, const void*))ffStrbufEqual) < state->configDirs.length - 1) \
{ \
ffStrbufDestroy(ffListGet(&state->configDirs, state->configDirs.length - 1)); \
--state->configDirs.length; \
@@ -48,6 +52,8 @@ static void initConfigDirs(FFstate* state)
ffStrbufEnsureEndsWithC(userHome, '/');
FF_ENSURE_ONLY_ONCE_IN_LIST(userHome)
+ #if !(defined(_WIN32) || defined(__APPLE__) || defined(__ANDROID__))
+
FFstrbuf xdgConfigDirs;
ffStrbufInitA(&xdgConfigDirs, 64);
ffStrbufAppendS(&xdgConfigDirs, getenv("XDG_CONFIG_DIRS"));
@@ -79,6 +85,8 @@ static void initConfigDirs(FFstate* state)
ffStrbufAppendS(systemConfigHome, FASTFETCH_TARGET_DIR_ETC"/xdg/");
FF_ENSURE_ONLY_ONCE_IN_LIST(systemConfigHome)
+ #endif
+
FFstrbuf* systemConfig = ffListAdd(&state->configDirs);
ffStrbufInitA(systemConfig, 64);
ffStrbufAppendS(systemConfig, FASTFETCH_TARGET_DIR_ETC"/");
@@ -87,47 +95,31 @@ static void initConfigDirs(FFstate* state)
#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)
{
+ #ifdef WIN32
+ //https://learn.microsoft.com/en-us/cpp/c-runtime-library/reference/setlocale-wsetlocale?source=recommendations&view=msvc-170#utf-8-support
+ setlocale(LC_ALL, ".UTF8");
+ #endif
+
state->logoWidth = 0;
state->logoHeight = 0;
state->keysHeight = 0;
- state->passwd = getpwuid(getuid());
+ #ifndef WIN32
+ state->passwd = getpwuid(getuid());
+ #else
+ state->passwd = ffGetPasswd();
+ #endif
uname(&state->utsname);
- #if FF_HAVE_SYSINFO_H
- sysinfo(&state->sysinfo);
- #endif
-
initConfigDirs(state);
- initCacheDir(state);
}
static void initModuleArg(FFModuleArgs* args)
{
- ffStrbufInitA(&args->key, 0);
- ffStrbufInitA(&args->outputFormat, 0);
- ffStrbufInitA(&args->errorFormat, 0);
+ ffStrbufInit(&args->key);
+ ffStrbufInit(&args->outputFormat);
+ ffStrbufInit(&args->errorFormat);
}
static void defaultConfig(FFinstance* instance)
@@ -141,6 +133,13 @@ static void defaultConfig(FFinstance* instance)
instance->config.logo.paddingLeft = 0;
instance->config.logo.paddingRight = 4;
instance->config.logo.printRemaining = true;
+ instance->config.logo.preserveAspectRadio = false;
+
+ instance->config.logo.chafaFgOnly = false;
+ ffStrbufInitS(&instance->config.logo.chafaSymbols, "block+border+space-wide-inverted"); // Chafa default
+ instance->config.logo.chafaCanvasMode = UINT32_MAX;
+ instance->config.logo.chafaColorSpace = UINT32_MAX;
+ instance->config.logo.chafaDitherMode = UINT32_MAX;
ffStrbufInit(&instance->config.colorKeys);
ffStrbufInit(&instance->config.colorTitle);
@@ -150,7 +149,6 @@ static void defaultConfig(FFinstance* instance)
instance->config.showErrors = false;
instance->config.recache = false;
- instance->config.cacheSave = true;
instance->config.allowSlowOperations = false;
instance->config.disableLinewrap = true;
instance->config.hideCursor = true;
@@ -159,9 +157,13 @@ static void defaultConfig(FFinstance* instance)
instance->config.glType = FF_GL_TYPE_AUTO;
instance->config.pipe = false;
instance->config.multithreading = true;
+ instance->config.stat = false;
initModuleArg(&instance->config.os);
initModuleArg(&instance->config.host);
+ initModuleArg(&instance->config.bios);
+ initModuleArg(&instance->config.board);
+ initModuleArg(&instance->config.chassis);
initModuleArg(&instance->config.kernel);
initModuleArg(&instance->config.uptime);
initModuleArg(&instance->config.processes);
@@ -189,6 +191,7 @@ static void defaultConfig(FFinstance* instance)
initModuleArg(&instance->config.localIP);
initModuleArg(&instance->config.publicIP);
initModuleArg(&instance->config.weather);
+ initModuleArg(&instance->config.wifi);
initModuleArg(&instance->config.player);
initModuleArg(&instance->config.song);
initModuleArg(&instance->config.dateTime);
@@ -221,6 +224,7 @@ static void defaultConfig(FFinstance* instance)
ffStrbufInitA(&instance->config.libOpenCL, 0);
ffStrbufInitA(&instance->config.libcJSON, 0);
ffStrbufInitA(&instance->config.libfreetype, 0);
+ ffStrbufInit(&instance->config.libwlanapi);
instance->config.cpuTemp = false;
instance->config.gpuTemp = false;
@@ -229,7 +233,8 @@ static void defaultConfig(FFinstance* instance)
instance->config.titleFQDN = false;
ffStrbufInitA(&instance->config.diskFolders, 0);
- instance->config.diskRemovable = false;
+ instance->config.diskShowRemovable = true;
+ instance->config.diskShowHidden = false;
ffStrbufInitA(&instance->config.batteryDir, 0);
@@ -249,6 +254,8 @@ static void defaultConfig(FFinstance* instance)
ffStrbufInitA(&instance->config.osFile, 0);
ffStrbufInitA(&instance->config.playerName, 0);
+
+ instance->config.percentType = 1;
}
void ffInitInstance(FFinstance* instance)
@@ -257,87 +264,39 @@ void ffInitInstance(FFinstance* instance)
defaultConfig(instance);
}
-#if !defined(__ANDROID__)
+#ifdef FF_HAVE_THREADS
-static void* connectDisplayServerThreadMain(void* instance)
-{
- ffConnectDisplayServer((FFinstance*)instance);
- return NULL;
-}
+FF_THREAD_ENTRY_DECL_WRAPPER(ffConnectDisplayServer, FFinstance*)
-#if !defined(__APPLE__)
+#if !(defined(__APPLE__) || defined(_WIN32))
-static void* detectPlasmaThreadMain(void* instance)
-{
- ffDetectQt((FFinstance*)instance);
- return NULL;
-}
+#define FF_DETECT_QT_GTK 1
-static void* detectGTK2ThreadMain(void* instance)
-{
- ffDetectGTK2((FFinstance*)instance);
- return NULL;
-}
+FF_THREAD_ENTRY_DECL_WRAPPER(ffDetectQt, FFinstance*)
+FF_THREAD_ENTRY_DECL_WRAPPER(ffDetectGTK2, FFinstance*)
+FF_THREAD_ENTRY_DECL_WRAPPER(ffDetectGTK3, FFinstance*)
+FF_THREAD_ENTRY_DECL_WRAPPER(ffDetectGTK4, FFinstance*)
-static void* detectGTK3ThreadMain(void* instance)
-{
- ffDetectGTK3((FFinstance*)instance);
- return NULL;
-}
+#endif //!(defined(__APPLE__) || defined(_WIN32))
-static void* detectGTK4ThreadMain(void* instance)
-{
- ffDetectGTK4((FFinstance*)instance);
- return NULL;
-}
-
-static 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;
-}
+#endif //FF_HAVE_THREADS
void startDetectionThreads(FFinstance* instance)
{
- pthread_t startThreadsThread;
- pthread_create(&startThreadsThread, NULL, startThreadsThreadMain, instance);
- pthread_detach(startThreadsThread);
-}
+ #ifdef FF_HAVE_THREADS
+ ffThreadDetach(ffThreadCreate(ffConnectDisplayServerThreadMain, instance));
-#else // !__APPLE__
-void startDetectionThreads(FFinstance* instance)
-{
- pthread_t startThreadsThread;
- pthread_create(&startThreadsThread, NULL, connectDisplayServerThreadMain, instance);
- pthread_detach(startThreadsThread);
-}
-#endif // __APPLE__
+ #ifdef FF_DETECT_QT_GTK
+ ffThreadDetach(ffThreadCreate(ffDetectQtThreadMain, instance));
+ ffThreadDetach(ffThreadCreate(ffDetectGTK2ThreadMain, instance));
+ ffThreadDetach(ffThreadCreate(ffDetectGTK3ThreadMain, instance));
+ ffThreadDetach(ffThreadCreate(ffDetectGTK4ThreadMain, instance));
+ #endif
-#else // !__ANDROID__
-void startDetectionThreads(FFinstance* instance)
-{
+ #else
FF_UNUSED(instance);
+ #endif
}
-#endif // __ANDROID__
static volatile bool ffDisableLinewrap = true;
static volatile bool ffHideCursor = true;
@@ -351,12 +310,21 @@ static void resetConsole()
fputs("\033[?25h", stdout);
}
+#ifdef _WIN32
+BOOL WINAPI consoleHandler(DWORD signal)
+{
+ FF_UNUSED(signal);
+ resetConsole();
+ exit(0);
+}
+#else
static void exitSignalHandler(int signal)
{
FF_UNUSED(signal);
resetConsole();
exit(0);
}
+#endif
void ffStart(FFinstance* instance)
{
@@ -366,16 +334,25 @@ void ffStart(FFinstance* instance)
ffDisableLinewrap = instance->config.disableLinewrap && !instance->config.pipe;
ffHideCursor = instance->config.hideCursor && !instance->config.pipe;
- struct sigaction action = {};
- action.sa_handler = exitSignalHandler;
-
+ #ifdef _WIN32
+ #ifdef FF_ENABLE_BUFFER
+ setvbuf(stdout, NULL, _IOFBF, 4096);
+ #endif
+ SetConsoleCtrlHandler(consoleHandler, TRUE);
+ HANDLE hStdout = GetStdHandle(STD_OUTPUT_HANDLE);
+ DWORD mode = 0;
+ GetConsoleMode(hStdout, &mode);
+ SetConsoleMode(hStdout, mode | ENABLE_PROCESSED_OUTPUT | ENABLE_VIRTUAL_TERMINAL_PROCESSING);
+ SetConsoleOutputCP(CP_UTF8);
+ #else
+ #ifndef FF_ENABLE_BUFFER
+ setvbuf(stdout, NULL, _IONBF, 0);
+ #endif
+ struct sigaction 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);
+ #endif
//reset everything to default before we start printing
if(!instance->config.pipe)
@@ -408,6 +385,7 @@ static void destroyModuleArg(FFModuleArgs* args)
static void destroyConfig(FFinstance* instance)
{
ffStrbufDestroy(&instance->config.logo.source);
+ ffStrbufDestroy(&instance->config.logo.chafaSymbols);
for(uint8_t i = 0; i < (uint8_t) FASTFETCH_LOGO_MAX_COLORS; ++i)
ffStrbufDestroy(&instance->config.logo.colors[i]);
ffStrbufDestroy(&instance->config.colorKeys);
@@ -416,6 +394,8 @@ static void destroyConfig(FFinstance* instance)
destroyModuleArg(&instance->config.os);
destroyModuleArg(&instance->config.host);
+ destroyModuleArg(&instance->config.bios);
+ destroyModuleArg(&instance->config.board);
destroyModuleArg(&instance->config.kernel);
destroyModuleArg(&instance->config.uptime);
destroyModuleArg(&instance->config.processes);
@@ -443,6 +423,7 @@ static void destroyConfig(FFinstance* instance)
destroyModuleArg(&instance->config.localIP);
destroyModuleArg(&instance->config.publicIP);
destroyModuleArg(&instance->config.weather);
+ destroyModuleArg(&instance->config.wifi);
destroyModuleArg(&instance->config.player);
destroyModuleArg(&instance->config.song);
destroyModuleArg(&instance->config.dateTime);
@@ -475,6 +456,7 @@ static void destroyConfig(FFinstance* instance)
ffStrbufDestroy(&instance->config.libOpenCL);
ffStrbufDestroy(&instance->config.libcJSON);
ffStrbufDestroy(&instance->config.libfreetype);
+ ffStrbufDestroy(&instance->config.libwlanapi);
ffStrbufDestroy(&instance->config.diskFolders);
ffStrbufDestroy(&instance->config.batteryDir);
@@ -491,8 +473,6 @@ static void destroyState(FFinstance* instance)
for(uint32_t i = 0; i < instance->state.configDirs.length; ++i)
ffStrbufDestroy((FFstrbuf*)ffListGet(&instance->state.configDirs, i));
ffListDestroy(&instance->state.configDirs);
-
- ffStrbufDestroy(&instance->state.cacheDir);
}
void ffDestroyInstance(FFinstance* instance)
diff --git a/src/common/io.c b/src/common/io.c
index 1b3ccddf9..1c2fb918b 100644
--- a/src/common/io.c
+++ b/src/common/io.c
@@ -1,11 +1,15 @@
#include "fastfetch.h"
#include "common/io.h"
-#include
#include
#include
-#include
-#include
+
+#ifdef _WIN32
+ #include
+#else
+ #include
+ #include
+#endif
static void createSubfolders(const char* fileName)
{
@@ -16,7 +20,13 @@ static void createSubfolders(const char* fileName)
{
ffStrbufAppendC(&path, *fileName);
if(*fileName == '/')
- mkdir(path.chars, S_IRWXU | S_IRGRP | S_IROTH);
+ {
+ mkdir(path.chars
+ #ifndef WIN32
+ , S_IRWXU | S_IRGRP | S_IROTH
+ #endif
+ );
+ }
++fileName;
}
@@ -30,6 +40,21 @@ bool ffWriteFDBuffer(int fd, const FFstrbuf* content)
bool ffWriteFileData(const char* fileName, size_t dataSize, const void* data)
{
+ #ifdef _WIN32
+ HANDLE handle = CreateFileA(fileName, GENERIC_WRITE, FILE_SHARE_WRITE, NULL, CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, NULL);
+ if(handle == INVALID_HANDLE_VALUE)
+ {
+ createSubfolders(fileName);
+ handle = CreateFileA(fileName, GENERIC_WRITE, FILE_SHARE_WRITE, NULL, CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, NULL);
+ if(handle == INVALID_HANDLE_VALUE)
+ return false;
+ }
+
+ DWORD written;
+ bool ret = !!WriteFile(handle, data, (DWORD)dataSize, &written, NULL);
+
+ CloseHandle(handle);
+ #else
int openFlagsModes = O_WRONLY | O_CREAT | O_TRUNC;
int openFlagsRights = S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH;
@@ -45,6 +70,7 @@ bool ffWriteFileData(const char* fileName, size_t dataSize, const void* data)
bool ret = write(fd, data, dataSize) != -1;
close(fd);
+ #endif
return ret;
}
@@ -54,11 +80,51 @@ bool ffWriteFileBuffer(const char* fileName, const FFstrbuf* buffer)
return ffWriteFileData(fileName, buffer->length, buffer->chars);
}
+#ifdef _WIN32
+
+bool ffAppendHandleBuffer(HANDLE handle, FFstrbuf* buffer)
+{
+ DWORD readed = 0;
+
+ LARGE_INTEGER fileSize;
+ if(!GetFileSizeEx(handle, &fileSize))
+ fileSize.QuadPart = 0;
+
+ ffStrbufEnsureFree(buffer, fileSize.QuadPart > 0 ? (uint32_t)fileSize.QuadPart : 31);
+ uint32_t free = ffStrbufGetFree(buffer);
+
+ bool success;
+ while(
+ (success = !!ReadFile(handle, buffer->chars + buffer->length, free, &readed, NULL)) &&
+ (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);
+ }
+
+ if(readed > 0)
+ buffer->length += (uint32_t) readed;
+
+ buffer->chars[buffer->length] = '\0';
+
+ ffStrbufTrimRight(buffer, '\n');
+ ffStrbufTrimRight(buffer, ' ');
+
+ return success;
+}
+
+#endif
+
bool ffAppendFDBuffer(int fd, FFstrbuf* buffer)
{
ssize_t readed = 0;
- ffStrbufEnsureFree(buffer, 31); // 32 - 1 for the null terminator
+ struct stat fileInfo;
+ if(fstat(fd, &fileInfo) != 0)
+ return false;
+
+ ffStrbufEnsureFree(buffer, fileInfo.st_size > 0 ? (uint32_t)fileInfo.st_size : 31);
uint32_t free = ffStrbufGetFree(buffer);
while(
@@ -84,6 +150,17 @@ bool ffAppendFDBuffer(int fd, FFstrbuf* buffer)
ssize_t ffReadFileData(const char* fileName, size_t dataSize, void* data)
{
+ #ifdef _WIN32
+ HANDLE handle = CreateFileA(fileName, GENERIC_READ, FILE_SHARE_READ, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL);
+ if(handle == INVALID_HANDLE_VALUE)
+ return false;
+
+ DWORD readed;
+ if(!ReadFile(handle, data, (DWORD)dataSize, &readed, NULL))
+ return -1;
+
+ return (ssize_t)readed;
+ #else
int fd = open(fileName, O_RDONLY);
if(fd == -1)
return -1;
@@ -93,10 +170,20 @@ ssize_t ffReadFileData(const char* fileName, size_t dataSize, void* data)
close(fd);
return readed;
+ #endif
}
bool ffAppendFileBuffer(const char* fileName, FFstrbuf* buffer)
{
+ #ifdef _WIN32
+ HANDLE handle = CreateFileA(fileName, GENERIC_READ, FILE_SHARE_READ, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL);
+ if(handle == INVALID_HANDLE_VALUE)
+ return false;
+
+ bool ret = ffAppendHandleBuffer(handle, buffer);
+
+ CloseHandle(handle);
+ #else
int fd = open(fileName, O_RDONLY);
if(fd == -1)
return false;
@@ -104,6 +191,7 @@ bool ffAppendFileBuffer(const char* fileName, FFstrbuf* buffer)
bool ret = ffAppendFDBuffer(fd, buffer);
close(fd);
+ #endif
return ret;
}
@@ -151,6 +239,7 @@ bool ffFileExists(const char* fileName, mode_t mode)
void ffGetTerminalResponse(const char* request, const char* format, ...)
{
+ #ifndef WIN32
struct termios oldTerm, newTerm;
if(tcgetattr(STDIN_FILENO, &oldTerm) == -1)
return;
@@ -189,4 +278,8 @@ void ffGetTerminalResponse(const char* request, const char* format, ...)
va_start(args, format);
vsscanf(buffer, format, args);
va_end(args);
+ #else
+ //Unimplemented
+ FF_UNUSED(request, format);
+ #endif
}
diff --git a/src/common/io.h b/src/common/io.h
index 35a70675f..b5446f455 100644
--- a/src/common/io.h
+++ b/src/common/io.h
@@ -3,7 +3,9 @@
#ifndef FF_INCLUDED_common_io
#define FF_INCLUDED_common_io
-#include //mode_t
+#include "fastfetch.h"
+
+#include //mode_t, fstat, stat
bool ffWriteFDBuffer(int fd, const FFstrbuf* content);
bool ffWriteFileData(const char* fileName, size_t dataSize, const void* data);
@@ -14,6 +16,10 @@ 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);
+#ifdef _WIN32
+bool ffAppendHandleBuffer(HANDLE handle, FFstrbuf* buffer);
+#endif
+
bool ffFileExists(const char* fileName, mode_t mode);
// Not thread safe!
diff --git a/src/common/library.c b/src/common/library.c
index 441b9f611..e771b8d4d 100644
--- a/src/common/library.c
+++ b/src/common/library.c
@@ -10,15 +10,25 @@
#endif
#endif
-#ifdef __SANITIZE_ADDRESS__
- #define FF_DLOPEN_FLAGS RTLD_LAZY | RTLD_NODELETE
-#else
- #define FF_DLOPEN_FLAGS RTLD_LAZY
+#ifndef FF_DLOPEN_FLAGS
+ #ifdef __SANITIZE_ADDRESS__
+ #define FF_DLOPEN_FLAGS RTLD_LAZY | RTLD_NODELETE
+ #else
+ #define FF_DLOPEN_FLAGS RTLD_LAZY
+ #endif
#endif
static void* libraryLoad(const char* path, int maxVersion)
{
void* result = dlopen(path, FF_DLOPEN_FLAGS);
+
+ #ifdef _WIN32
+
+ // libX.dll.1 never exists on Windows, while libX-1.dll may exist
+ FF_UNUSED(maxVersion)
+
+ #else
+
if(result != NULL || maxVersion < 0)
return result;
@@ -40,6 +50,9 @@ static void* libraryLoad(const char* path, int maxVersion)
}
ffStrbufDestroy(&pathbuf);
+
+ #endif
+
return result;
}
diff --git a/src/common/library.h b/src/common/library.h
index 25a323119..c23aa7b53 100644
--- a/src/common/library.h
+++ b/src/common/library.h
@@ -3,9 +3,22 @@
#ifndef FF_INCLUDED_common_library
#define FF_INCLUDED_common_library
-#include
+#include "fastfetch.h"
+#include "util/FFcheckmacros.h"
-#ifdef __APPLE__
+#if defined(_WIN32) //We don't force MSYS using LoadLibrary because dlopen also searches $LD_LIBRARY_PATH
+ #include
+ #define FF_DLOPEN_FLAGS 0
+ FF_C_NODISCARD static inline void* dlopen(const char* path, int mode) { FF_UNUSED(mode); return LoadLibraryA(path); }
+ FF_C_NODISCARD static inline void* dlsym(void* handle, const char* symbol) { return GetProcAddress((HMODULE)handle, symbol); }
+ static inline int dlclose(void* handle) { return !FreeLibrary((HMODULE)handle); }
+#else
+ #include
+#endif
+
+#ifdef _WIN32
+ #define FF_LIBRARY_EXTENSION ".dll"
+#elif defined(__APPLE__)
#define FF_LIBRARY_EXTENSION ".dylib"
#else
#define FF_LIBRARY_EXTENSION ".so"
@@ -30,6 +43,9 @@
#define FF_LIBRARY_LOAD_SYMBOL(library, symbolName, returnValue) \
__typeof__(&symbolName) FF_LIBRARY_LOAD_SYMBOL_ADDRESS(library, ff ## symbolName, symbolName, returnValue);
+#define FF_LIBRARY_LOAD_SYMBOL_LAZY(library, symbolName) \
+ __typeof__(&symbolName) ff ## symbolName = dlsym(library, #symbolName);
+
#define FF_LIBRARY_LOAD_SYMBOL_MESSAGE(library, symbolName) \
__typeof__(&symbolName) FF_LIBRARY_LOAD_SYMBOL_ADDRESS(library, ff ## symbolName, symbolName, "dlsym " #symbolName " failed");
diff --git a/src/common/networking.c b/src/common/networking.c
deleted file mode 100644
index 4558f7f8e..000000000
--- a/src/common/networking.c
+++ /dev/null
@@ -1,85 +0,0 @@
-#include "fastfetch.h"
-#include "common/networking.h"
-
-#include
-#include
-#include
-#include
-
-int ffNetworkingSendHttpRequest(const char* host, const char* path, const char* headers, uint32_t timeout)
-{
- struct addrinfo hints = {
- .ai_family = AF_INET,
- .ai_socktype = SOCK_STREAM,
- };
-
- struct addrinfo* addr;
-
- if(getaddrinfo(host, "80", &hints, &addr) != 0)
- return -1;
-
- int sockfd = socket(addr->ai_family, addr->ai_socktype, addr->ai_protocol);
- if(sockfd == -1)
- {
- freeaddrinfo(addr);
- return -1;
- }
-
- if(timeout > 0)
- {
- struct timeval timev;
- timev.tv_sec = 0;
- timev.tv_usec = (__typeof__(timev.tv_usec)) (timeout * 1000); //milliseconds to microseconds
- setsockopt(sockfd, SOL_SOCKET, SO_RCVTIMEO, &timev, sizeof(timev));
- }
-
- if(connect(sockfd, addr->ai_addr, addr->ai_addrlen) == -1)
- {
- close(sockfd);
- freeaddrinfo(addr);
- return -1;
- }
-
- 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");
- ffStrbufAppendS(&command, headers);
- ffStrbufAppendS(&command, "\r\n");
-
- if(send(sockfd, command.chars, command.length, 0) == -1)
- {
- ffStrbufDestroy(&command);
- close(sockfd);
- return -1;
- }
- ffStrbufDestroy(&command);
- return sockfd;
-}
-
-bool ffNetworkingRecvHttpResponse(int sockfd, FFstrbuf* buffer)
-{
- ssize_t received = recv(sockfd, buffer->chars + buffer->length, ffStrbufGetFree(buffer), 0);
-
- if(received > 0)
- {
- buffer->length += (uint32_t) received;
- buffer->chars[buffer->length] = '\0';
- }
-
- close(sockfd);
- return ffStrbufStartsWithS(buffer, "HTTP/1.1 200 OK\r\n");
-}
-
-bool ffNetworkingGetHttp(const char* host, const char* path, uint32_t timeout, const char* headers, FFstrbuf* buffer)
-{
- int sockfd = ffNetworkingSendHttpRequest(host, path, headers, timeout);
- if(sockfd > 0)
- return ffNetworkingRecvHttpResponse(sockfd, buffer);
- return false;
-}
diff --git a/src/common/networking.h b/src/common/networking.h
index 145493ce7..f27850477 100644
--- a/src/common/networking.h
+++ b/src/common/networking.h
@@ -3,10 +3,37 @@
#ifndef FF_INCLUDED_common_networking
#define FF_INCLUDED_common_networking
+#include "common/thread.h"
#include "util/FFstrbuf.h"
-int ffNetworkingSendHttpRequest(const char* host, const char* path, const char* headers, uint32_t timeout);
-bool ffNetworkingRecvHttpResponse(int sock, FFstrbuf* buffer);
-bool ffNetworkingGetHttp(const char* host, const char* path, uint32_t timeout, const char* headers, FFstrbuf* buffer);
+#ifdef _WIN32
+ #include
+#endif
+
+typedef struct FFNetworkingState {
+ #ifdef _WIN32
+ uintptr_t sockfd;
+ OVERLAPPED overlapped;
+ #else
+ int sockfd;
+ FFstrbuf host;
+ FFstrbuf command;
+
+ #ifdef FF_HAVE_THREADS
+ FFThreadType thread;
+ #endif
+ #endif
+} FFNetworkingState;
+
+bool ffNetworkingSendHttpRequest(FFNetworkingState* state, const char* host, const char* path, const char* headers);
+bool ffNetworkingRecvHttpResponse(FFNetworkingState* state, FFstrbuf* buffer, uint32_t timeout);
+
+static inline bool ffNetworkingGetHttp(const char* host, const char* path, uint32_t timeout, const char* headers, FFstrbuf* buffer)
+{
+ FFNetworkingState state;
+ if(ffNetworkingSendHttpRequest(&state, host, path, headers))
+ return ffNetworkingRecvHttpResponse(&state, buffer, timeout);
+ return false;
+}
#endif
diff --git a/src/common/networking_linux.c b/src/common/networking_linux.c
new file mode 100644
index 000000000..7fb6fade5
--- /dev/null
+++ b/src/common/networking_linux.c
@@ -0,0 +1,103 @@
+#include "fastfetch.h"
+#include "common/networking.h"
+
+#include
+#include
+#include
+#include
+
+static void connectAndSend(FFNetworkingState* state)
+{
+ struct addrinfo hints = {
+ .ai_family = AF_INET,
+ .ai_socktype = SOCK_STREAM,
+ };
+
+ struct addrinfo* addr;
+
+ if(getaddrinfo(state->host.chars, "80", &hints, &addr) != 0)
+ goto error;
+
+ state->sockfd = socket(addr->ai_family, addr->ai_socktype, addr->ai_protocol);
+ if(state->sockfd == -1)
+ {
+ freeaddrinfo(addr);
+ goto error;
+ }
+
+ if(connect(state->sockfd, addr->ai_addr, addr->ai_addrlen) == -1)
+ {
+ close(state->sockfd);
+ freeaddrinfo(addr);
+ goto error;
+ }
+
+ freeaddrinfo(addr);
+
+ if(send(state->sockfd, state->command.chars, state->command.length, 0) < 0)
+ {
+ close(state->sockfd);
+ goto error;
+ }
+
+ goto exit;
+
+error:
+ state->sockfd = -1;
+
+exit:
+ ffStrbufDestroy(&state->host);
+ ffStrbufDestroy(&state->command);
+}
+
+FF_THREAD_ENTRY_DECL_WRAPPER(connectAndSend, FFNetworkingState*);
+
+bool ffNetworkingSendHttpRequest(FFNetworkingState* state, const char* host, const char* path, const char* headers)
+{
+ ffStrbufInitS(&state->host, host);
+
+ ffStrbufInitA(&state->command, 64);
+ ffStrbufAppendS(&state->command, "GET ");
+ ffStrbufAppendS(&state->command, path);
+ ffStrbufAppendS(&state->command, " HTTP/1.1\nHost: ");
+ ffStrbufAppendS(&state->command, host);
+ ffStrbufAppendS(&state->command, "\r\n");
+ ffStrbufAppendS(&state->command, headers);
+ ffStrbufAppendS(&state->command, "\r\n");
+
+ #ifdef FF_HAVE_THREADS
+ state->thread = ffThreadCreate(connectAndSendThreadMain, state);
+ return !!state->thread;
+ #else
+ connectAndSend(state);
+ return state->sockfd != -1;
+ #endif
+}
+
+bool ffNetworkingRecvHttpResponse(FFNetworkingState* state, FFstrbuf* buffer, uint32_t timeout)
+{
+ #ifdef FF_HAVE_THREADS
+ ffThreadJoin(state->thread);
+ #endif
+ if(state->sockfd == -1)
+ return false;
+
+ if(timeout > 0)
+ {
+ struct timeval timev;
+ timev.tv_sec = 0;
+ timev.tv_usec = (__typeof__(timev.tv_usec)) (timeout * 1000); //milliseconds to microseconds
+ setsockopt(state->sockfd, SOL_SOCKET, SO_RCVTIMEO, &timev, sizeof(timev));
+ }
+
+ ssize_t received = recv(state->sockfd, buffer->chars + buffer->length, ffStrbufGetFree(buffer), 0);
+
+ if(received > 0)
+ {
+ buffer->length += (uint32_t) received;
+ buffer->chars[buffer->length] = '\0';
+ }
+
+ close(state->sockfd);
+ return ffStrbufStartsWithS(buffer, "HTTP/1.1 200 OK\r\n");
+}
diff --git a/src/common/networking_windows.c b/src/common/networking_windows.c
new file mode 100644
index 000000000..5814acc16
--- /dev/null
+++ b/src/common/networking_windows.c
@@ -0,0 +1,125 @@
+#include
+#include
+
+//Must be included after
+#include "fastfetch.h"
+#include "common/networking.h"
+
+static LPFN_CONNECTEX ConnectEx;
+
+static BOOL WINAPI initWsaData(PINIT_ONCE once, PVOID param, PVOID* context)
+{
+ (void)once;
+ (void)param;
+ static WSADATA wsaData;
+ *context = &wsaData;
+ if(WSAStartup(MAKEWORD(2, 2), &wsaData) != 0)
+ return FALSE;
+
+ if(LOBYTE(wsaData.wVersion) != 2 || HIBYTE(wsaData.wVersion) != 2)
+ return FALSE;
+
+ //Dummy socket needed for WSAIoctl
+ SOCKET sockfd = socket(AF_INET, SOCK_STREAM, 0);
+ if(sockfd == INVALID_SOCKET)
+ return FALSE;
+
+ DWORD dwBytes;
+ GUID guid = WSAID_CONNECTEX;
+ if(WSAIoctl(sockfd, SIO_GET_EXTENSION_FUNCTION_POINTER,
+ &guid, sizeof(guid),
+ &ConnectEx, sizeof(ConnectEx),
+ &dwBytes, NULL, NULL) != 0)
+ return FALSE;
+
+ return closesocket(sockfd) == 0;
+}
+
+bool ffNetworkingSendHttpRequest(FFNetworkingState* state, const char* host, const char* path, const char* headers)
+{
+ static INIT_ONCE once = INIT_ONCE_STATIC_INIT;
+ WSADATA* pData;
+ if(!InitOnceExecuteOnce(&once, initWsaData, NULL, (LPVOID*) &pData))
+ return false;
+
+ memset(state, 0, sizeof(*state));
+
+ struct addrinfo hints = {
+ .ai_family = AF_INET,
+ .ai_socktype = SOCK_STREAM,
+ };
+
+ struct addrinfo* addr;
+
+ if(getaddrinfo(host, "80", &hints, &addr) != 0)
+ return false;
+
+ state->sockfd = socket(addr->ai_family, addr->ai_socktype, addr->ai_protocol);
+ if(state->sockfd == INVALID_SOCKET)
+ {
+ freeaddrinfo(addr);
+ return false;
+ }
+
+ {
+ //ConnectEx requires the socket to be initially bound
+ struct sockaddr_in addr = {
+ .sin_family = AF_INET,
+ .sin_addr.s_addr = INADDR_ANY,
+ .sin_port = 0,
+ };
+ if(bind(state->sockfd, (SOCKADDR *)&addr, sizeof(addr)) != 0)
+ {
+ printf("bind %d\n", WSAGetLastError());
+ return false;
+ }
+ }
+
+ FF_STRBUF_AUTO_DESTROY command;
+ ffStrbufInitA(&command, 64);
+ ffStrbufAppendS(&command, "GET ");
+ ffStrbufAppendS(&command, path);
+ ffStrbufAppendS(&command, " HTTP/1.1\nHost: ");
+ ffStrbufAppendS(&command, host);
+ ffStrbufAppendS(&command, "\r\n");
+ ffStrbufAppendS(&command, headers);
+ ffStrbufAppendS(&command, "\r\n");
+
+ BOOL result = ConnectEx(state->sockfd, addr->ai_addr, (int)addr->ai_addrlen, command.chars, command.length, NULL, &state->overlapped);
+ freeaddrinfo(addr);
+
+ if(!result && WSAGetLastError() != WSA_IO_PENDING)
+ {
+ closesocket(state->sockfd);
+ return false;
+ }
+
+ return true;
+}
+
+bool ffNetworkingRecvHttpResponse(FFNetworkingState* state, FFstrbuf* buffer, uint32_t timeout)
+{
+ DWORD transfer, flags;
+ if (!WSAGetOverlappedResult(state->sockfd, &state->overlapped, &transfer, TRUE, &flags))
+ {
+ closesocket(state->sockfd);
+ return false;
+ }
+
+ if(timeout > 0)
+ {
+ //https://learn.microsoft.com/en-us/windows/win32/api/winsock2/nf-winsock2-setsockopt
+ setsockopt(state->sockfd, SOL_SOCKET, SO_RCVTIMEO, (const char*)&timeout, sizeof(timeout));
+ }
+
+ ssize_t received = recv(state->sockfd, buffer->chars + buffer->length, (int)ffStrbufGetFree(buffer), 0);
+
+ if(received > 0)
+ {
+ buffer->length += (uint32_t) received;
+ buffer->chars[buffer->length] = '\0';
+ }
+
+ closesocket(state->sockfd);
+ return ffStrbufStartsWithS(buffer, "HTTP/1.1 200 OK\r\n");
+}
diff --git a/src/common/printing.c b/src/common/printing.c
index a6911db15..4b593001a 100644
--- a/src/common/printing.c
+++ b/src/common/printing.c
@@ -1,5 +1,6 @@
#include "fastfetch.h"
#include "common/printing.h"
+#include "util/textModifier.h"
void ffPrintLogoAndKey(FFinstance* instance, const char* moduleName, uint8_t moduleIndex, const FFstrbuf* customKeyFormat)
{
@@ -93,7 +94,9 @@ static void printError(FFinstance* instance, const char* moduleName, uint8_t mod
vprintf(message, arguments);
if(!instance->config.pipe)
- puts(FASTFETCH_TEXT_MODIFIER_RESET);
+ fputs(FASTFETCH_TEXT_MODIFIER_RESET, stdout);
+
+ putchar('\n');
}
}
@@ -127,8 +130,13 @@ void ffPrintColor(const FFstrbuf* colorValue)
void ffPrintCharTimes(char c, uint32_t times)
{
- for(uint32_t i = 0; i < times; i++)
- putchar(c);
+ char str[32];
+ memset(str, c, sizeof(str)); //2 instructions when compiling with AVX2 enabled
+ for(uint32_t i = sizeof(str); i <= times; i += sizeof(str))
+ fwrite(str, 1, sizeof(str), stdout);
+ uint32_t remaining = times % sizeof(str);
+ if(remaining > 0)
+ fwrite(str, 1, remaining, stdout);
}
void ffPrintUserString(const char* value)
diff --git a/src/common/processing.c b/src/common/processing_linux.c
similarity index 100%
rename from src/common/processing.c
rename to src/common/processing_linux.c
diff --git a/src/common/processing_windows.c b/src/common/processing_windows.c
new file mode 100644
index 000000000..30e515734
--- /dev/null
+++ b/src/common/processing_windows.c
@@ -0,0 +1,69 @@
+#include "fastfetch.h"
+#include "common/processing.h"
+
+#define WIN32_LEAN_AND_MEAN 1
+#include
+
+//We can't use this native version yet because we still use POSIX path ( eg /usr/bin/fish ) at a lot of places.
+const char* ffProcessAppendStdOut(FFstrbuf* buffer, char* const argv[])
+{
+ SECURITY_ATTRIBUTES saAttr = {
+ .nLength = sizeof(SECURITY_ATTRIBUTES),
+ .lpSecurityDescriptor = NULL,
+ .bInheritHandle = TRUE,
+ };
+
+ HANDLE hChildStdoutRead, hChildStdoutWrite;
+ if (!CreatePipe(&hChildStdoutRead, &hChildStdoutWrite, &saAttr, 0))
+ return "CreatePipe() failed";
+
+ if (!SetHandleInformation(hChildStdoutRead, HANDLE_FLAG_INHERIT, 0))
+ return "SetHandleInformation(hChildStdoutRead) failed";
+
+ PROCESS_INFORMATION piProcInfo = {0};
+ STARTUPINFOA siStartInfo = {
+ .cb = sizeof(siStartInfo),
+ .dwFlags = STARTF_USESTDHANDLES,
+ .hStdOutput = hChildStdoutWrite,
+ };
+
+ BOOL success;
+
+ {
+ FF_STRBUF_AUTO_DESTROY cmdline;
+ ffStrbufInitF(&cmdline, "\"%s\"", argv[0]);
+ for(char* const* parg = &argv[1]; *parg; ++parg)
+ {
+ ffStrbufAppendC(&cmdline, ' ');
+ ffStrbufAppendS(&cmdline, *parg);
+ }
+
+ success = CreateProcessA(
+ NULL, // application name
+ cmdline.chars, // command line
+ NULL, // process security attributes
+ NULL, // primary thread security attributes
+ TRUE, // handles are inherited
+ 0, // creation flags
+ NULL, // use parent's environment
+ NULL, // use parent's current directory
+ &siStartInfo, // STARTUPINFO pointer
+ &piProcInfo // receives PROCESS_INFORMATION
+ );
+ }
+
+ CloseHandle(hChildStdoutWrite);
+ if(!success)
+ {
+ CloseHandle(hChildStdoutRead);
+ return "CreateProcessA() failed";
+ }
+
+ char str[1024];
+ DWORD nRead;
+ while(ReadFile(hChildStdoutRead, str, sizeof(str), &nRead, NULL) && nRead > 0)
+ ffStrbufAppendNS(buffer, nRead, str);
+
+ CloseHandle(hChildStdoutRead);
+ return NULL;
+}
diff --git a/src/common/properties.c b/src/common/properties.c
index 8331c6b3f..064d620aa 100644
--- a/src/common/properties.c
+++ b/src/common/properties.c
@@ -2,6 +2,9 @@
#include "common/properties.h"
#include
+#ifdef _WIN32
+ #include "util/windows/getline.h"
+#endif
static bool parsePropLinePointer(const char** line, const char* start, FFstrbuf* buffer)
{
diff --git a/src/common/properties.h b/src/common/properties.h
index 38d79c2ec..dfdcad96e 100644
--- a/src/common/properties.h
+++ b/src/common/properties.h
@@ -3,7 +3,7 @@
#ifndef FF_INCLUDED_common_properties
#define FF_INCLUDED_common_properties
-#include "util/FFstrbuf.h"
+#include "fastfetch.h"
typedef struct FFpropquery
{
diff --git a/src/common/settings.c b/src/common/settings.c
index 1b8a08c5d..38cbd4b36 100644
--- a/src/common/settings.c
+++ b/src/common/settings.c
@@ -1,9 +1,9 @@
#include "fastfetch.h"
#include "common/settings.h"
#include "common/library.h"
+#include "common/thread.h"
#include "common/io.h"
-#include
#include
typedef enum FFInitState
@@ -15,18 +15,18 @@ typedef enum FFInitState
#define FF_LIBRARY_DATA_LOAD_INIT(dataObject, userLibraryName, ...) \
static dataObject data; \
- static pthread_mutex_t mutex = PTHREAD_MUTEX_INITIALIZER; \
+ static FFThreadMutex mutex = FF_THREAD_MUTEX_INITIALIZER; \
static FFInitState initState = FF_INITSTATE_UNINITIALIZED; \
- pthread_mutex_lock(&mutex); \
+ ffThreadMutexLock(&mutex); \
if(initState != FF_INITSTATE_UNINITIALIZED) {\
- pthread_mutex_unlock(&mutex); \
+ ffThreadMutexUnlock(&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); \
+ ffThreadMutexUnlock(&mutex); \
return NULL; \
} \
@@ -35,20 +35,20 @@ typedef enum FFInitState
if(data.ff ## symbolName == NULL) { \
dlclose(libraryHandle); \
initState = FF_INITSTATE_FAILED; \
- pthread_mutex_unlock(&mutex); \
+ ffThreadMutexUnlock(&mutex); \
return NULL; \
}
#define FF_LIBRARY_DATA_LOAD_RETURN \
initState = FF_INITSTATE_SUCCESSFUL; \
- pthread_mutex_unlock(&mutex); \
+ ffThreadMutexUnlock(&mutex); \
return &data;
#define FF_LIBRARY_DATA_LOAD_ERROR \
{ \
dlclose(libraryHandle); \
initState = FF_INITSTATE_FAILED; \
- pthread_mutex_unlock(&mutex); \
+ ffThreadMutexUnlock(&mutex); \
return NULL; \
}
@@ -91,7 +91,7 @@ typedef struct GSettingsData
static const GSettingsData* getGSettingsData(const FFinstance* instance)
{
- FF_LIBRARY_DATA_LOAD_INIT(GSettingsData, instance->config.libGIO, "libgio-2.0.so", 1);
+ FF_LIBRARY_DATA_LOAD_INIT(GSettingsData, instance->config.libGIO, "libgio-2.0" FF_LIBRARY_EXTENSION, 1);
FF_LIBRARY_DATA_LOAD_SYMBOL(g_settings_schema_source_lookup)
FF_LIBRARY_DATA_LOAD_SYMBOL(g_settings_schema_has_key)
@@ -163,7 +163,7 @@ typedef struct DConfData
static const DConfData* getDConfData(const FFinstance* instance)
{
- FF_LIBRARY_DATA_LOAD_INIT(DConfData, instance->config.libDConf, "libdconf.so", 2);
+ FF_LIBRARY_DATA_LOAD_INIT(DConfData, instance->config.libDConf, "libdconf" FF_LIBRARY_EXTENSION, 2);
FF_LIBRARY_DATA_LOAD_SYMBOL(dconf_client_read_full)
FF_LIBRARY_DATA_LOAD_SYMBOL(dconf_client_new)
@@ -233,7 +233,7 @@ typedef struct XFConfData
static const XFConfData* getXFConfData(const FFinstance* instance)
{
- FF_LIBRARY_DATA_LOAD_INIT(XFConfData, instance->config.libXFConf, "libxfconf-0.so", 4);
+ FF_LIBRARY_DATA_LOAD_INIT(XFConfData, instance->config.libXFConf, "libxfconf-0" FF_LIBRARY_EXTENSION, 4);
FF_LIBRARY_DATA_LOAD_SYMBOL(xfconf_channel_get)
FF_LIBRARY_DATA_LOAD_SYMBOL(xfconf_channel_has_property)
@@ -280,7 +280,6 @@ FFvariant ffSettingsGetXFConf(const FFinstance* instance, const char* channelNam
#ifdef FF_HAVE_SQLITE3
#include
-#include
typedef struct SQLiteData
{
@@ -295,7 +294,7 @@ typedef struct SQLiteData
static const SQLiteData* getSQLiteData(const FFinstance* instance)
{
- FF_LIBRARY_DATA_LOAD_INIT(SQLiteData, instance->config.libSQLite3, "libsqlite3.so", 1);
+ FF_LIBRARY_DATA_LOAD_INIT(SQLiteData, instance->config.libSQLite3, "libsqlite3" FF_LIBRARY_EXTENSION, 1);
FF_LIBRARY_DATA_LOAD_SYMBOL(sqlite3_open_v2)
FF_LIBRARY_DATA_LOAD_SYMBOL(sqlite3_prepare_v2)
diff --git a/src/common/sysctl.c b/src/common/sysctl.c
index 30e92383a..4f0f3b65c 100644
--- a/src/common/sysctl.c
+++ b/src/common/sysctl.c
@@ -2,11 +2,11 @@
#include
-void ffSysctlGetString(const char* propName, FFstrbuf* result)
+const char* ffSysctlGetString(const char* propName, FFstrbuf* result)
{
size_t neededLength;
if(sysctlbyname(propName, NULL, &neededLength, NULL, 0) != 0 || neededLength == 1) //neededLength is 1 for empty strings, because of the null terminator
- return;
+ return "sysctlbyname() failed";
ffStrbufEnsureFree(result, (uint32_t) neededLength - 1);
@@ -14,6 +14,8 @@ void ffSysctlGetString(const char* propName, FFstrbuf* result)
result->length += (uint32_t) neededLength - 1;
result->chars[result->length] = '\0';
+
+ return NULL;
}
int ffSysctlGetInt(const char* propName, int defaultValue)
diff --git a/src/common/sysctl.h b/src/common/sysctl.h
index 91e4ad8b6..562e625d5 100644
--- a/src/common/sysctl.h
+++ b/src/common/sysctl.h
@@ -4,12 +4,14 @@
#define FF_INCLUDED_common_sysctl
#include "fastfetch.h"
+#include "util/FFcheckmacros.h"
+
#include
#include
-void ffSysctlGetString(const char* propName, FFstrbuf* result);
-int ffSysctlGetInt(const char* propName, int defaultValue);
-int64_t ffSysctlGetInt64(const char* propName, int64_t defaultValue);
-void* ffSysctlGetData(int* request, u_int requestLength, size_t* resultLength);
+const char* ffSysctlGetString(const char* propName, FFstrbuf* result);
+FF_C_NODISCARD int ffSysctlGetInt(const char* propName, int defaultValue);
+FF_C_NODISCARD int64_t ffSysctlGetInt64(const char* propName, int64_t defaultValue);
+FF_C_NODISCARD void* ffSysctlGetData(int* request, u_int requestLength, size_t* resultLength);
#endif
diff --git a/src/common/thread.h b/src/common/thread.h
new file mode 100644
index 000000000..c4d223084
--- /dev/null
+++ b/src/common/thread.h
@@ -0,0 +1,48 @@
+#pragma once
+
+#ifndef FF_INCLUDED_common_thread
+#define FF_INCLUDED_common_thread
+
+#include "fastfetch.h"
+
+#ifdef FF_HAVE_THREADS
+ #if defined(_WIN32)
+ #include
+ #include
+ #include
+ #define FF_THREAD_MUTEX_INITIALIZER SRWLOCK_INIT
+ typedef SRWLOCK FFThreadMutex;
+ typedef HANDLE FFThreadType;
+ static inline void ffThreadMutexLock(FFThreadMutex* mutex) { AcquireSRWLockExclusive(mutex); }
+ static inline void ffThreadMutexUnlock(FFThreadMutex* mutex) { ReleaseSRWLockExclusive(mutex); }
+ static inline FFThreadType ffThreadCreate(unsigned (__stdcall* func)(void*), void* data) {
+ return (FFThreadType)_beginthreadex(NULL, 0, func, data, 0, NULL);
+ }
+ #define FF_THREAD_ENTRY_DECL_WRAPPER(fn, paramType) static __stdcall unsigned fn ## ThreadMain (void* data) { fn((paramType)data); return 0; }
+ static inline void ffThreadDetach(FFThreadType thread) { CloseHandle(thread); }
+ static inline void ffThreadJoin(FFThreadType thread) { WaitForSingleObject(thread, INFINITE); }
+ #else
+ #include
+ #define FF_THREAD_MUTEX_INITIALIZER PTHREAD_MUTEX_INITIALIZER
+ typedef pthread_mutex_t FFThreadMutex;
+ typedef pthread_t FFThreadType;
+ static inline void ffThreadMutexLock(FFThreadMutex* mutex) { pthread_mutex_lock(mutex); }
+ static inline void ffThreadMutexUnlock(FFThreadMutex* mutex) { pthread_mutex_unlock(mutex); }
+ static inline FFThreadType ffThreadCreate(void* (* func)(void*), void* data) {
+ FFThreadType newThread = 0;
+ pthread_create(&newThread, NULL, func, data);
+ return newThread;
+ }
+ #define FF_THREAD_ENTRY_DECL_WRAPPER(fn, paramType) static void* fn ## ThreadMain (void* data) { fn((paramType)data); return NULL; }
+ static inline void ffThreadDetach(FFThreadType thread) { pthread_detach(thread); }
+ static inline void ffThreadJoin(FFThreadType thread) { pthread_join(thread, NULL); }
+ #endif
+#else //FF_HAVE_THREADS
+ #define FF_THREAD_MUTEX_INITIALIZER 0
+ typedef char FFThreadMutex;
+ static inline void ffThreadMutexLock(FFThreadMutex* mutex) { FF_UNUSED(mutex) }
+ static inline void ffThreadMutexUnlock(FFThreadMutex* mutex) { FF_UNUSED(mutex) }
+ #define FF_THREAD_ENTRY_DECL_WRAPPER(fn, paramType)
+#endif //FF_HAVE_THREADS
+
+#endif
diff --git a/src/common/time.h b/src/common/time.h
new file mode 100644
index 000000000..797461cbd
--- /dev/null
+++ b/src/common/time.h
@@ -0,0 +1,38 @@
+#pragma once
+
+#ifndef FF_INCLUDED_common_time
+#define FF_INCLUDED_common_time
+
+#include
+#ifdef _WIN32
+ #include
+ #include
+#else
+ #include
+#endif
+
+static inline uint64_t ffTimeGetTick() //In msec
+{
+ #ifdef _WIN32
+ LARGE_INTEGER frequency;
+ QueryPerformanceFrequency(&frequency);
+ LARGE_INTEGER start;
+ QueryPerformanceCounter(&start);
+ return (uint64_t)(start.QuadPart * 1000 / frequency.QuadPart);
+ #else
+ struct timespec timeNow;
+ clock_gettime(CLOCK_MONOTONIC, &timeNow);
+ return (uint64_t)((timeNow.tv_sec * 1000) + (timeNow.tv_nsec / 1000000));
+ #endif
+}
+
+static inline void ffTimeSleep(uint32_t msec)
+{
+ #ifdef _WIN32
+ SleepEx(msec, TRUE);
+ #else
+ nanosleep(&(struct timespec){ msec / 1000, (msec % 1000) * 1000000 }, NULL);
+ #endif
+}
+
+#endif
diff --git a/src/data/config_user.txt b/src/data/config_user.txt
index 26b90679f..acf7f2117 100644
--- a/src/data/config_user.txt
+++ b/src/data/config_user.txt
@@ -5,7 +5,7 @@
# 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$".
+# 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.
@@ -25,7 +25,7 @@
# 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$"
+#--structure $" FASTFETCH_DATATEXT_STRUCTURE $"
# Multithreading option:
# Sets if fastfetch should use multiple threads to detect the values.
@@ -33,6 +33,13 @@
# Default is true.
#--multithreading true
+# Print stat option:
+# Sets if fastfetch should print time usage (in ms) for individual modules
+# If true, it will also enable --show-errors
+# Must be true or false.
+# Default is false.
+#--stat true
+
# Slow operations option:
# Sets if fastfetch is allowed to use known slow operations to detect more / better values.
# Must be true or false.
@@ -61,7 +68,7 @@
# Logo type option:
# Sets the logo type to use.
-# Must be auto, builtin, file, raw, sixel, kitty or chafa.
+# Must be auto, builtin, file, file-raw, data, data-raw, sixel, kitty or chafa.
# Default is auto.
#--logo-type auto
@@ -178,7 +185,6 @@
# 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
@@ -200,11 +206,18 @@
# Default is auto.
#--gl auto
+# Percentage output type option
+# Applies to all modules that prints percentage values. Currently memory, swap, disk, battery and CPU usage are supported.
+# Only works with default format ( without --module-format option ).
+# 0: prints none; 1: prints percent number only; 2: prints bar only; 3: prints both percent number and bar
+#--percent-type 1
+
# 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
+#--chassis-key Chassis
#--kernel-key Kernel
#--uptime-key Uptime
#--processes-key Processes
@@ -231,6 +244,7 @@
#--locale-key Locale
#--local-ip-key Local IP ({1})
#--public-ip-key Public IP
+#--wifi-key Wifi
#--weather-key Weather
#--player-key Media Player
#--song-key Song
@@ -247,6 +261,7 @@
# An empty format string (As they are currently below) will behave as if it was not set.
#--os-format
#--host-format
+#--chassis-format
#--kernel-format
#--uptime-format
#--processes-format
@@ -289,6 +304,7 @@
# If one of them is set, the module will appear, even if --show-errors is not given.
#--os-error
#--host-error
+#--chassis-error
#--kernel-error
#--uptime-error
#--processes-error
@@ -328,7 +344,7 @@
# 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-vulkan /usr/lib/libvulkan.so (libMoltenVK.dylib on macOS)
#--lib-wayland /usr/lib/libwayland-client.so
#--lib-xcb-randr /usr/lib/libxcb-randr.so
#--lib-xcb /usr/lib/libxcb.so
diff --git a/src/data/help.txt b/src/data/help.txt
index f24242ec4..dea266a24 100644
--- a/src/data/help.txt
+++ b/src/data/help.txt
@@ -1,65 +1,73 @@
Usage: fastfetch
Informative options:
- -h,--help: shows this message and exits
- -h,--help : 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
+ -h,--help: Show this message
+ -h,--help : Show help for a specific command
+ -v,--version: Show the version of fastfetch
+ --list-logos: List available logos
+ --list-modules: List available modules
+ --list-presets: List presets fastfetch knows about; they can be loaded with --load-config (+)
+ --list-features: List the supported features fastfetch was compiled with (mainly for development)
+ --print-logos: Print available logos
+ --print-config-system: Print the default system config
+ --print-config-user: Print the default user config
+ --print-structure: Print the default structure
General options:
- -r,--recache : generate new cached values
- --nocache : don't use cached values, but also don't overwrite existing ones
- --load-config : load a config file or a preset (+)
- --multithreading : use multiple threads to detect values
- --allow-slow-operations : allow operations that are usually very slow for more detailed output
- --escape-bedrock : on bedrock linux, sets if it should escape the bedrock jail or not
- --pipe : disable logo and all escape sequences
+ --load-config : Load a config file or preset (+)
+ --multithreading : Use multiple threads to detect values
+ --stat : Show time usage (in ms) for individual modules
+ --allow-slow-operations : Allow operations that are usually very slow for more detailed output
+ --escape-bedrock : On Bedrock Linux, whether to escape the bedrock jail
+ --pipe : Disable logo and all escape sequences
Logo options:
- -l,--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 : set the type of the logo given. Must be auto, builtin, file, raw, sixel, kitty or chafa.
- --logo-width : set the width of the logo (in characters), if it is an image
- --logo-height : set the height of the logo (in characters), if it is an image
- --logo-color-[1-9] : overwrite a color in the logo
- --logo-padding : set the padding on the left and the right of the logo
- --logo-padding-left : set the padding on the left of the logo
- --logo-padding-right : set the padding on the right of the logo
- --logo-print-remaining : weather to print the remaining logo, if it has more lines than modules to display
- --sixel : short for --logo-type sixel --logo
- --kitty : short for --logo-type kitty --logo
- --chafa : short for --logo-type chafa --logo
- --file : short for --logo-type file --logo
- --raw : short for --logo-type raw --logo
+ -l,--logo : Set the logo; if default, the name of a builtin logo or a path to a file
+ --logo-type : Set the type of the logo given; must be auto, builtin, file, file-raw, data, data-raw, sixel, kitty, iterm or chafa
+ --logo-width : Set the width of the logo (in characters), if it is an image. Required for iTerm image protocol
+ --logo-height : Set the height of the logo (in characters), if it is an image. Required for iTerm image protocol
+ --logo-preserve-aspect-radio : Set if the logo should fill the specified width and height as much as possible without stretching. Supported by iTerm image protocol
+ --logo-color-[1-9] : Overwrite a color in the logo
+ --logo-padding : Set the padding on the left and the right of the logo
+ --logo-padding-left : Set the padding on the left of the logo
+ --logo-padding-right : Set the padding on the right of the logo
+ --logo-print-remaining : Whether to print the remaining logo, if it has more lines than modules to display
+ --file : Short for --logo-type file --logo
+ --file-raw : Short for --logo-type file-raw --logo
+ --data : Short for --logo-type data --logo
+ --data-raw : Short for --logo-type data-raw --logo
+ --sixel : Short for --logo-type sixel --logo
+ --kitty : Short for --logo-type kitty --logo
+ --iterm : Short for --logo-type iterm --logo
+ --chafa : Short for --logo-type chafa --logo
+ --chafa-fg-only : Produce character-cell output using foreground colors only. See chafa document for detail
+ --chafa-symbols : Specify character symbols to employ in final output. See chafa document for detail
+ --chafa-canvas-mode : Determine how colors are used in the output. This value maps the int value of enum ChafaCanvasMode. See chafa document for detail
+ --chafa-color-space : Set color space used for quantization. 0 for RGB; 1 for DIN99d. See chafa document for detail
+ --chafa-dither-mode : Set output dither mode (No effect with 24-bit color). This value maps the int value of enum ChafaDitherMode. See chafa document for detail
Display options:
- -s,--structure : sets the structure of the fetch. Must be a colon separated list of keys. Use "fastfetch --list-modules" to see the ones available.
- --color-keys : sers the color of the keys
- --color-title : sets the color of the title
- -c,--color : sets the color of both the keys and the title
- --separator : sets the separator between key and value. Default is a colon with a space
- --set : hard set the value of a key
- --set-keyless : hard set the value of a key, but don't print the key or the separator
- --show-errors : print occuring errors
- --disable-linewrap : weather to disable linewrap during the run
- --hide-cursor : weather to hide the cursor during the run
- --binary-prefix : sets the binary prefix to used. Must be IEC, SI or JEDEC. Default is IEC
+ -s,--structure : Set the structure of the fetch. Must be a colon separated list of keys. Use "fastfetch --list-modules" to see the ones available.
+ --color-keys : Set the color of the keys
+ --color-title : Set the color of the title
+ -c,--color : Set the color of both the keys and title
+ --separator : Set the separator between key and value. Default is a colon with a space
+ --set : Hard set the value of a key
+ --set-keyless : Hard set the value of a key, but don't print the key or the separator
+ --show-errors : Print occurring errors
+ --disable-linewrap : Whether to disable line wrap during the run
+ --hide-cursor : Whether to hide the cursor during the run
+ --binary-prefix : Set the binary prefix to used. Must be IEC, SI or JEDEC. Default is IEC.
General module options:
- ---format : Sets the format string to use for each specific module.
+ ---format : Set 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 -format.
- ---key : Sets the key to use for each specific module.
+ ---key : Set 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.
- ---error : Sets the error format string to use for each specific module.
+ ---error : Set 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.
@@ -87,25 +95,27 @@ Library options: Set the path of a library to load
--lib-cjson
Module specific options:
- --title-fqdn : Sets if the title should use fully qualified domain name. Default is false.
- --separator-string : Set the string printed by the separator module
- --os-file : Set the path to the file containing OS informations
- --disk-folders : A colon separated list of folder paths for the disk output. Default is "/:/home"
- --disk-removable : Sets if removable volume should be printed. Default is false
- --battery-dir : The directory where the battery folders are. Standard: /sys/class/power_supply/
- --cpu-temp : Detect and display CPU temperature if supported. Default is false
- --gpu-temp : Detect and display GPU temperature if supported. Default is false
- --battery-temp : Detect and display Battery temperature if supported. Default is false
- --localip-show-ipv4 : Show ipv4 addresses in local ip module. Default is true
- --localip-show-ipv6 : Show ipv6 addresses in local ip module. Default is false
- --localip-show-loop : Show loop back addresses (127.0.0.1) in local ip module. Default is false
- --localip-name-prefix : Show ips with given name prefix only. Default is empty
- --public-ip-timeout: Time in milliseconds to wait for the public ip server to respond. Default is disabled (0)
- --public-ip-url: The URL of public IP detection server to be used.
- --weather-timeout: Time in milliseconds to wait for the weather server to respond. Default is disabled (0)
- --weather-output-format: The output weather format to be used. It must be URI encoded.
- --player-name: The name of the player to use
- --gl : Sets the opengl context creation library to use. Must be auto, egl, glx or osmesa. Default is auto
+ --title-fqdn : Set if the title should use fully qualified domain name. Default is false
+ --separator-string : Set the string printed by the separator module
+ --os-file : Set the path to the file containing OS information
+ --disk-folders : A colon (semicolon on Windows) separated list of folder paths for the disk output. Default is "/:/home" ("C:\\;D:\\ ..." on Windows)
+ --disk-show-removable : Set if removable volume should be printed. Default is true
+ --disk-show-hidden : Set if hidden volumes should be printed. Default is false
+ --battery-dir : The directory where the battery folders are. Standard: /sys/class/power_supply/
+ --cpu-temp : Detect and display CPU temperature if supported. Default is false
+ --gpu-temp : Detect and display GPU temperature if supported. Default is false
+ --battery-temp : Detect and display Battery temperature if supported. Default is false
+ --localip-show-ipv4 : Show IPv4 addresses in local ip module. Default is true
+ --localip-show-ipv6 : Show IPv6 addresses in local ip module. Default is false
+ --localip-show-loop : Show loop back addresses (127.0.0.1) in local ip module. Default is false
+ --localip-name-prefix : Show IPs with given name prefix only. Default is empty
+ --public-ip-timeout: Time in milliseconds to wait for the public ip server to respond. Default is disabled (0)
+ --public-ip-url: The URL of public IP detection server to be used.
+ --weather-timeout: Time in milliseconds to wait for the weather server to respond. Default is disabled (0)
+ --weather-output-format: The output weather format to be used. It must be URI encoded.
+ --player-name: The name of the player to use
+ --gl : Set the OpenGL context creation library to use. Must be auto, egl, glx or osmesa. Default is auto
+ --percent-type : Set the percentage output type. 1 for percentage number, 2 for bar, 3 for both. Default is 1
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.
diff --git a/src/data/modules.txt b/src/data/modules.txt
index 0177c4b5d..4420e2c91 100644
--- a/src/data/modules.txt
+++ b/src/data/modules.txt
@@ -1,5 +1,8 @@
Battery
+Bios
+Board
Break
+Chassis
Colors
CPU
CPUUsage
@@ -15,19 +18,18 @@ Icons
Kernel
Locale
LocalIP
-Media
Memory
-OS
-OpenCL
OpenGL
+OS
Packages
Player
+PowerAdapter
Processes
PublicIP
-PowerAdapter
Resolution
Separator
Shell
+Song
Swap
Terminal
TerminalFont
@@ -36,5 +38,7 @@ Time
Title
Uptime
Vulkan
+Weather
+Wifi
WM
WMTheme
diff --git a/src/detection/battery/battery.h b/src/detection/battery/battery.h
index 6269f590a..ab0021b91 100644
--- a/src/detection/battery/battery.h
+++ b/src/detection/battery/battery.h
@@ -12,7 +12,7 @@ typedef struct BatteryResult
FFstrbuf manufacturer;
FFstrbuf modelName;
FFstrbuf technology;
- FFstrbuf capacity;
+ double capacity;
FFstrbuf status;
double temperature;
} BatteryResult;
diff --git a/src/detection/battery/battery_apple.c b/src/detection/battery/battery_apple.c
index 0e9667cf7..7244a89a1 100644
--- a/src/detection/battery/battery_apple.c
+++ b/src/detection/battery/battery_apple.c
@@ -7,7 +7,7 @@
static double detectBatteryTemp()
{
- FFlist temps;
+ FF_LIST_AUTO_DESTROY temps;
ffListInit(&temps, sizeof(FFTempValue));
ffDetectCoreTemps(FF_TEMP_BATTERY, &temps);
@@ -25,7 +25,6 @@ static double detectBatteryTemp()
ffStrbufDestroy(&tempValue->deviceClass);
}
result /= temps.length;
- ffListDestroy(&temps);
return result;
}
@@ -55,7 +54,7 @@ const char* ffDetectBatteryImpl(FFinstance* instance, FFlist* results)
const char* error;
BatteryResult* battery = ffListAdd(results);
- ffStrbufInit(&battery->capacity);
+ battery->capacity = 0.0/0.0;
int currentCapacity, maxCapacity;
if ((error = ffCfDictGetInt(properties, CFSTR("MaxCapacity"), &maxCapacity)))
@@ -68,7 +67,7 @@ const char* ffDetectBatteryImpl(FFinstance* instance, FFlist* results)
if(currentCapacity <= 0)
return "Querying CurrentCapacity failed";
- ffStrbufAppendF(&battery->capacity, "%.0f", currentCapacity * 100.0 / maxCapacity);
+ battery->capacity = currentCapacity * 100.0 / maxCapacity;
ffStrbufInit(&battery->manufacturer);
ffStrbufInit(&battery->modelName);
diff --git a/src/detection/battery/battery_linux.c b/src/detection/battery/battery_linux.c
index 6a8643521..6fe799eb0 100644
--- a/src/detection/battery/battery_linux.c
+++ b/src/detection/battery/battery_linux.c
@@ -33,18 +33,18 @@ static void parseBattery(FFstrbuf* dir, FFlist* results)
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);
+ bool available = ffReadFileBuffer(dir->chars, &testBatteryBuffer);
ffStrbufSubstrBefore(dir, dirLength);
-
- if(result->capacity.length == 0)
+ if(available)
+ result->capacity = ffStrbufToDouble(&testBatteryBuffer);
+ ffStrbufDestroy(&testBatteryBuffer);
+ if(!available)
{
- ffStrbufDestroy(&result->capacity);
+ result->capacity = 0.0/0.0;
--results->length;
return;
}
diff --git a/src/detection/battery/battery_android.c b/src/detection/battery/battery_nosupport.c
similarity index 64%
rename from src/detection/battery/battery_android.c
rename to src/detection/battery/battery_nosupport.c
index 2fc2b1d85..40ca05241 100644
--- a/src/detection/battery/battery_android.c
+++ b/src/detection/battery/battery_nosupport.c
@@ -1,10 +1,8 @@
#include "fastfetch.h"
-#include "common/io.h"
#include "battery.h"
-#include
-
-const char* ffDetectBatteryImpl(FFinstance* instance, FFlist* results) {
+const char* ffDetectBatteryImpl(FFinstance* instance, FFlist* results)
+{
FF_UNUSED(instance, results)
- return "Unimplemented";
+ return "Not supported on this platform";
}
diff --git a/src/detection/battery/battery_windows.c b/src/detection/battery/battery_windows.c
new file mode 100644
index 000000000..ca8c60991
--- /dev/null
+++ b/src/detection/battery/battery_windows.c
@@ -0,0 +1,158 @@
+#include "battery.h"
+#include "util/windows/unicode.h"
+#include "util/mallocHelper.h"
+
+#include
+#include
+#include
+#include
+#include
+
+static inline void wrapCloseHandle(HANDLE* handle)
+{
+ if(*handle)
+ CloseHandle(*handle);
+}
+static inline void wrapSetupDiDestroyDeviceInfoList(HDEVINFO* hdev)
+{
+ if(*hdev)
+ SetupDiDestroyDeviceInfoList(*hdev);
+}
+
+const char* ffDetectBatteryImpl(FFinstance* instance, FFlist* results)
+{
+ if(instance->config.allowSlowOperations)
+ {
+ //https://learn.microsoft.com/en-us/windows/win32/power/enumerating-battery-devices
+ HDEVINFO hdev __attribute__((__cleanup__(wrapSetupDiDestroyDeviceInfoList))) =
+ SetupDiGetClassDevs(&GUID_DEVCLASS_BATTERY, 0, 0, DIGCF_PRESENT | DIGCF_DEVICEINTERFACE);
+ if(hdev == INVALID_HANDLE_VALUE)
+ return "SetupDiGetClassDevs(&GUID_DEVCLASS_BATTERY) failed";
+
+ for(DWORD idev = 0;; idev++)
+ {
+ SP_DEVICE_INTERFACE_DATA did = { .cbSize = sizeof(did) };
+ if(!SetupDiEnumDeviceInterfaces(hdev, NULL, &GUID_DEVCLASS_BATTERY, idev, &did))
+ break;
+
+ DWORD cbRequired = 0;
+ SetupDiGetDeviceInterfaceDetailW(hdev, &did, NULL, 0, &cbRequired, NULL); //Fail with not enough buffer
+ SP_DEVICE_INTERFACE_DETAIL_DATA_W* FF_AUTO_FREE pdidd = (SP_DEVICE_INTERFACE_DETAIL_DATA_W*)malloc(cbRequired);
+ if(!pdidd)
+ break; //Out of memory
+
+ pdidd->cbSize = sizeof(*pdidd);
+ if(!SetupDiGetDeviceInterfaceDetailW(hdev, &did, pdidd, cbRequired, &cbRequired, NULL))
+ continue;
+
+ HANDLE __attribute__((__cleanup__(wrapCloseHandle))) hBattery =
+ CreateFileW(pdidd->DevicePath, GENERIC_READ, FILE_SHARE_READ, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL);
+
+ if(hBattery == INVALID_HANDLE_VALUE)
+ continue;
+
+ BATTERY_QUERY_INFORMATION bqi = { .InformationLevel = BatteryInformation };
+
+ DWORD dwWait = 0;
+ DWORD dwOut;
+
+ if(!DeviceIoControl(hBattery, IOCTL_BATTERY_QUERY_TAG, &dwWait, sizeof(dwWait), &bqi.BatteryTag, sizeof(bqi.BatteryTag), &dwOut, NULL) && bqi.BatteryTag)
+ continue;
+
+ BATTERY_INFORMATION bi = {0};
+ if(!DeviceIoControl(hBattery, IOCTL_BATTERY_QUERY_INFORMATION, &bqi, sizeof(bqi), &bi, sizeof(bi), &dwOut, NULL))
+ continue;
+
+ if(!(bi.Capabilities & BATTERY_SYSTEM_BATTERY))
+ continue;
+
+ BatteryResult* battery = (BatteryResult*)ffListAdd(results);
+
+ if(memcmp(bi.Chemistry, "PbAc", 4) == 0)
+ ffStrbufInitS(&battery->technology, "Lead Acid");
+ else if(memcmp(bi.Chemistry, "LION", 4) == 0 || memcmp(bi.Chemistry, "Li-I", 4) == 0)
+ ffStrbufInitS(&battery->technology, "Lithium Ion");
+ else if(memcmp(bi.Chemistry, "NiCd", 4) == 0)
+ ffStrbufInitS(&battery->technology, "Nickel Cadmium");
+ else if(memcmp(bi.Chemistry, "NiMH", 4) == 0)
+ ffStrbufInitS(&battery->technology, "Nickel Metal Hydride");
+ else if(memcmp(bi.Chemistry, "NiZn", 4) == 0)
+ ffStrbufInitS(&battery->technology, "Nickel Zinc");
+ else if(memcmp(bi.Chemistry, "RAM\0", 4) == 0)
+ ffStrbufInitS(&battery->technology, "Rechargeable Alkaline-Manganese");
+ else
+ ffStrbufInitS(&battery->technology, "Unknown");
+
+ {
+ ffStrbufInit(&battery->modelName);
+ bqi.InformationLevel = BatteryDeviceName;
+ wchar_t name[64];
+ if(DeviceIoControl(hBattery, IOCTL_BATTERY_QUERY_INFORMATION, &bqi, sizeof(bqi), name, sizeof(name), &dwOut, NULL))
+ ffStrbufSetWS(&battery->modelName, name);
+ }
+
+ {
+ ffStrbufInit(&battery->manufacturer);
+ bqi.InformationLevel = BatteryManufactureName;
+ wchar_t name[64];
+ if(DeviceIoControl(hBattery, IOCTL_BATTERY_QUERY_INFORMATION, &bqi, sizeof(bqi), name, sizeof(name), &dwOut, NULL))
+ ffStrbufSetWS(&battery->manufacturer, name);
+ }
+
+ battery->temperature = 0.0/0.0;
+ if(instance->config.batteryTemp)
+ {
+ bqi.InformationLevel = BatteryTemperature;
+ ULONG temp;
+ if(DeviceIoControl(hBattery, IOCTL_BATTERY_QUERY_INFORMATION, &bqi, sizeof(bqi), &temp, sizeof(temp), &dwOut, NULL))
+ battery->temperature = temp;
+ }
+
+ {
+ BATTERY_STATUS bs;
+ BATTERY_WAIT_STATUS bws = { .BatteryTag = bqi.BatteryTag };
+ if(DeviceIoControl(hBattery, IOCTL_BATTERY_QUERY_STATUS, &bws, sizeof(bws), &bs, sizeof(bs), &dwOut, NULL) && bs.Capacity != BATTERY_UNKNOWN_CAPACITY)
+ battery->capacity = bs.Capacity * 100.0 / bi.FullChargedCapacity;
+ else
+ battery->capacity = 0;
+
+ ffStrbufInit(&battery->status);
+ if(bs.PowerState & BATTERY_POWER_ON_LINE)
+ ffStrbufAppendS(&battery->status, "AC Connected, ");
+ if(bs.PowerState & BATTERY_DISCHARGING)
+ ffStrbufAppendS(&battery->status, "Discharging, ");
+ if(bs.PowerState & BATTERY_CHARGING)
+ ffStrbufAppendS(&battery->status, "Charging");
+ if(bs.PowerState & BATTERY_CRITICAL)
+ ffStrbufAppendS(&battery->status, "Critical, ");
+ ffStrbufTrimRight(&battery->status, ' ');
+ ffStrbufTrimRight(&battery->status, ',');
+ }
+ }
+ }
+ else
+ {
+ SYSTEM_BATTERY_STATE info;
+ if (NT_SUCCESS(CallNtPowerInformation(SystemBatteryState, NULL, 0, &info, sizeof(info))) && info.BatteryPresent)
+ {
+ BatteryResult* battery = (BatteryResult*)ffListAdd(results);
+ ffStrbufInit(&battery->modelName);
+ ffStrbufInit(&battery->manufacturer);
+ ffStrbufInit(&battery->technology);
+ ffStrbufInit(&battery->status);
+ battery->temperature = 0.0/0.0;
+
+ battery->capacity = info.RemainingCapacity * 100.0 / info.MaxCapacity;
+ if(info.AcOnLine)
+ {
+ ffStrbufAppendS(&battery->status, "AC Connected");
+ if(info.Charging)
+ ffStrbufAppendS(&battery->status, ", Charging");
+ }
+ else if(info.Discharging)
+ ffStrbufAppendS(&battery->status, "Discharging");
+ }
+ }
+
+ return NULL;
+}
diff --git a/src/detection/bios/bios.h b/src/detection/bios/bios.h
new file mode 100644
index 000000000..6f664b4b2
--- /dev/null
+++ b/src/detection/bios/bios.h
@@ -0,0 +1,19 @@
+#pragma once
+
+#ifndef FF_INCLUDED_detection_bios_bios
+#define FF_INCLUDED_detection_bios_bios
+
+#include "fastfetch.h"
+
+typedef struct FFBiosResult
+{
+ FFstrbuf biosDate;
+ FFstrbuf biosRelease;
+ FFstrbuf biosVendor;
+ FFstrbuf biosVersion;
+ FFstrbuf error;
+} FFBiosResult;
+
+void ffDetectBios(FFBiosResult* bios);
+
+#endif
diff --git a/src/detection/bios/bios_linux.c b/src/detection/bios/bios_linux.c
new file mode 100644
index 000000000..10bbab589
--- /dev/null
+++ b/src/detection/bios/bios_linux.c
@@ -0,0 +1,58 @@
+#include "bios.h"
+#include "common/io.h"
+
+#include
+
+static bool hostValueSet(FFstrbuf* value)
+{
+ return
+ value->length > 0 &&
+ ffStrbufStartsWithIgnCaseS(value, "To be filled") != true &&
+ ffStrbufStartsWithIgnCaseS(value, "To be set") != true &&
+ ffStrbufStartsWithIgnCaseS(value, "OEM") != true &&
+ ffStrbufStartsWithIgnCaseS(value, "O.E.M.") != true &&
+ ffStrbufIgnCaseCompS(value, "None") != 0 &&
+ ffStrbufIgnCaseCompS(value, "System Product") != 0 &&
+ ffStrbufIgnCaseCompS(value, "System Product Name") != 0 &&
+ ffStrbufIgnCaseCompS(value, "System Product Version") != 0 &&
+ ffStrbufIgnCaseCompS(value, "System Name") != 0 &&
+ ffStrbufIgnCaseCompS(value, "System Version") != 0 &&
+ ffStrbufIgnCaseCompS(value, "Default string") != 0 &&
+ ffStrbufIgnCaseCompS(value, "Undefined") != 0 &&
+ ffStrbufIgnCaseCompS(value, "Not Specified") != 0 &&
+ ffStrbufIgnCaseCompS(value, "Not Applicable") != 0 &&
+ ffStrbufIgnCaseCompS(value, "INVALID") != 0 &&
+ ffStrbufIgnCaseCompS(value, "Type1ProductConfigId") != 0 &&
+ ffStrbufIgnCaseCompS(value, "All Series") != 0
+ ;
+}
+
+static void getHostValue(const char* devicesPath, const char* classPath, FFstrbuf* buffer)
+{
+ ffReadFileBuffer(devicesPath, buffer);
+ if(hostValueSet(buffer))
+ return;
+
+ ffReadFileBuffer(classPath, buffer);
+ if(hostValueSet(buffer))
+ return;
+
+ ffStrbufClear(buffer);
+}
+
+void ffDetectBios(FFBiosResult* bios)
+{
+ ffStrbufInit(&bios->error);
+
+ ffStrbufInit(&bios->biosDate);
+ getHostValue("/sys/devices/virtual/dmi/id/bios_date", "/sys/class/dmi/id/bios_date", &bios->biosDate);
+
+ ffStrbufInit(&bios->biosRelease);
+ getHostValue("/sys/devices/virtual/dmi/id/bios_release", "/sys/class/dmi/id/bios_release", &bios->biosRelease);
+
+ ffStrbufInit(&bios->biosVendor);
+ getHostValue("/sys/devices/virtual/dmi/id/bios_vendor", "/sys/class/dmi/id/bios_vendor", &bios->biosVendor);
+
+ ffStrbufInit(&bios->biosVersion);
+ getHostValue("/sys/devices/virtual/dmi/id/bios_version", "/sys/class/dmi/id/bios_version", &bios->biosVersion);
+}
diff --git a/src/detection/bios/bios_nosupport.c b/src/detection/bios/bios_nosupport.c
new file mode 100644
index 000000000..f8d886182
--- /dev/null
+++ b/src/detection/bios/bios_nosupport.c
@@ -0,0 +1,11 @@
+#include "bios.h"
+
+void ffDetectBios(FFBiosResult* bios)
+{
+ ffStrbufInitS(&bios->error, "Not supported on this platform");
+
+ ffStrbufInit(&bios->biosDate);
+ ffStrbufInit(&bios->biosRelease);
+ ffStrbufInit(&bios->biosVendor);
+ ffStrbufInit(&bios->biosVersion);
+}
diff --git a/src/detection/bios/bios_windows.c b/src/detection/bios/bios_windows.c
new file mode 100644
index 000000000..7facb2576
--- /dev/null
+++ b/src/detection/bios/bios_windows.c
@@ -0,0 +1,28 @@
+#include "bios.h"
+#include "util/windows/registry.h"
+
+void ffDetectBios(FFBiosResult* bios)
+{
+ ffStrbufInit(&bios->error);
+
+ ffStrbufInit(&bios->biosDate);
+ ffStrbufInit(&bios->biosRelease);
+ ffStrbufInit(&bios->biosVendor);
+ ffStrbufInit(&bios->biosVersion);
+
+ FF_HKEY_AUTO_DESTROY hKey = NULL;
+ if(!ffRegOpenKeyForRead(HKEY_LOCAL_MACHINE, L"HARDWARE\\DESCRIPTION\\System\\BIOS", &hKey, &bios->error))
+ return;
+
+ if(!ffRegReadStrbuf(hKey, L"BIOSVersion", &bios->biosRelease, &bios->error))
+ return;
+ ffRegReadStrbuf(hKey, L"BIOSVendor", &bios->biosVendor, NULL);
+ ffRegReadStrbuf(hKey, L"BIOSReleaseDate", &bios->biosDate, NULL);
+
+ uint32_t major, minor;
+ if(
+ ffRegReadUint(hKey, L"BiosMajorRelease", &major, NULL) &&
+ ffRegReadUint(hKey, L"BiosMinorRelease", &minor, NULL)
+ )
+ ffStrbufAppendF(&bios->biosVersion, "%u.%u", (unsigned)major, (unsigned)minor);
+}
diff --git a/src/detection/board/board.h b/src/detection/board/board.h
new file mode 100644
index 000000000..15044469e
--- /dev/null
+++ b/src/detection/board/board.h
@@ -0,0 +1,18 @@
+#pragma once
+
+#ifndef FF_INCLUDED_detection_board_board
+#define FF_INCLUDED_detection_board_board
+
+#include "fastfetch.h"
+
+typedef struct FFBoardResult
+{
+ FFstrbuf boardName;
+ FFstrbuf boardVendor;
+ FFstrbuf boardVersion;
+ FFstrbuf error;
+} FFBoardResult;
+
+void ffDetectBoard(FFBoardResult* result);
+
+#endif
diff --git a/src/detection/board/board_linux.c b/src/detection/board/board_linux.c
new file mode 100644
index 000000000..8e92eec4e
--- /dev/null
+++ b/src/detection/board/board_linux.c
@@ -0,0 +1,55 @@
+#include "board.h"
+#include "common/io.h"
+
+#include
+
+static bool hostValueSet(FFstrbuf* value)
+{
+ return
+ value->length > 0 &&
+ ffStrbufStartsWithIgnCaseS(value, "To be filled") != true &&
+ ffStrbufStartsWithIgnCaseS(value, "To be set") != true &&
+ ffStrbufStartsWithIgnCaseS(value, "OEM") != true &&
+ ffStrbufStartsWithIgnCaseS(value, "O.E.M.") != true &&
+ ffStrbufIgnCaseCompS(value, "None") != 0 &&
+ ffStrbufIgnCaseCompS(value, "System Product") != 0 &&
+ ffStrbufIgnCaseCompS(value, "System Product Name") != 0 &&
+ ffStrbufIgnCaseCompS(value, "System Product Version") != 0 &&
+ ffStrbufIgnCaseCompS(value, "System Name") != 0 &&
+ ffStrbufIgnCaseCompS(value, "System Version") != 0 &&
+ ffStrbufIgnCaseCompS(value, "Default string") != 0 &&
+ ffStrbufIgnCaseCompS(value, "Undefined") != 0 &&
+ ffStrbufIgnCaseCompS(value, "Not Specified") != 0 &&
+ ffStrbufIgnCaseCompS(value, "Not Applicable") != 0 &&
+ ffStrbufIgnCaseCompS(value, "INVALID") != 0 &&
+ ffStrbufIgnCaseCompS(value, "Type1ProductConfigId") != 0 &&
+ ffStrbufIgnCaseCompS(value, "All Series") != 0
+ ;
+}
+
+static void getHostValue(const char* devicesPath, const char* classPath, FFstrbuf* buffer)
+{
+ ffReadFileBuffer(devicesPath, buffer);
+ if(hostValueSet(buffer))
+ return;
+
+ ffReadFileBuffer(classPath, buffer);
+ if(hostValueSet(buffer))
+ return;
+
+ ffStrbufClear(buffer);
+}
+
+void ffDetectBoard(FFBoardResult* board)
+{
+ ffStrbufInit(&board->error);
+
+ ffStrbufInit(&board->boardName);
+ getHostValue("/sys/devices/virtual/dmi/id/board_name", "/sys/class/dmi/id/board_name", &board->boardName);
+
+ ffStrbufInit(&board->boardVendor);
+ getHostValue("/sys/devices/virtual/dmi/id/board_vendor", "/sys/class/dmi/id/board_vendor", &board->boardVendor);
+
+ ffStrbufInit(&board->boardVersion);
+ getHostValue("/sys/devices/virtual/dmi/id/board_version", "/sys/class/dmi/id/board_version", &board->boardVersion);
+}
diff --git a/src/detection/board/board_nosupport.c b/src/detection/board/board_nosupport.c
new file mode 100644
index 000000000..0b182ba3e
--- /dev/null
+++ b/src/detection/board/board_nosupport.c
@@ -0,0 +1,10 @@
+#include "board.h"
+
+void ffDetectBoard(FFBoardResult* board)
+{
+ ffStrbufInitS(&board->error, "Not supported on this platform");
+
+ ffStrbufInit(&board->boardName);
+ ffStrbufInit(&board->boardVendor);
+ ffStrbufInit(&board->boardVersion);
+}
diff --git a/src/detection/board/board_windows.c b/src/detection/board/board_windows.c
new file mode 100644
index 000000000..4d02b679b
--- /dev/null
+++ b/src/detection/board/board_windows.c
@@ -0,0 +1,21 @@
+#include "board.h"
+#include "util/windows/registry.h"
+
+void ffDetectBoard(FFBoardResult* board)
+{
+ ffStrbufInit(&board->error);
+
+ ffStrbufInit(&board->boardName);
+ ffStrbufInit(&board->boardVendor);
+ ffStrbufInit(&board->boardVersion);
+
+ FF_HKEY_AUTO_DESTROY hKey = NULL;
+
+ if(!ffRegOpenKeyForRead(HKEY_LOCAL_MACHINE, L"HARDWARE\\DESCRIPTION\\System\\BIOS", &hKey, &board->error))
+ return;
+
+ if(!ffRegReadStrbuf(hKey, L"BaseBoardProduct", &board->boardName, &board->error))
+ return;
+ ffRegReadStrbuf(hKey, L"BaseBoardManufacturer", &board->boardVendor, NULL);
+ ffRegReadStrbuf(hKey, L"BaseBoardVersion", &board->boardVersion, NULL);
+}
diff --git a/src/detection/chassis/chassis.h b/src/detection/chassis/chassis.h
new file mode 100644
index 000000000..eb6ce4c6c
--- /dev/null
+++ b/src/detection/chassis/chassis.h
@@ -0,0 +1,18 @@
+#pragma once
+
+#ifndef FF_INCLUDED_detection_chassis_chassis
+#define FF_INCLUDED_detection_chassis_chassis
+
+#include "fastfetch.h"
+
+typedef struct FFChassisResult
+{
+ FFstrbuf chassisType;
+ FFstrbuf chassisVendor;
+ FFstrbuf chassisVersion;
+ FFstrbuf error;
+} FFChassisResult;
+
+void ffDetectChassis(FFChassisResult* result);
+
+#endif
diff --git a/src/detection/chassis/chassis_linux.c b/src/detection/chassis/chassis_linux.c
new file mode 100644
index 000000000..192747e7d
--- /dev/null
+++ b/src/detection/chassis/chassis_linux.c
@@ -0,0 +1,55 @@
+#include "chassis.h"
+#include "common/io.h"
+
+#include
+
+static bool hostValueSet(FFstrbuf* value)
+{
+ return
+ value->length > 0 &&
+ ffStrbufStartsWithIgnCaseS(value, "To be filled") != true &&
+ ffStrbufStartsWithIgnCaseS(value, "To be set") != true &&
+ ffStrbufStartsWithIgnCaseS(value, "OEM") != true &&
+ ffStrbufStartsWithIgnCaseS(value, "O.E.M.") != true &&
+ ffStrbufIgnCaseCompS(value, "None") != 0 &&
+ ffStrbufIgnCaseCompS(value, "System Product") != 0 &&
+ ffStrbufIgnCaseCompS(value, "System Product Name") != 0 &&
+ ffStrbufIgnCaseCompS(value, "System Product Version") != 0 &&
+ ffStrbufIgnCaseCompS(value, "System Name") != 0 &&
+ ffStrbufIgnCaseCompS(value, "System Version") != 0 &&
+ ffStrbufIgnCaseCompS(value, "Default string") != 0 &&
+ ffStrbufIgnCaseCompS(value, "Undefined") != 0 &&
+ ffStrbufIgnCaseCompS(value, "Not Specified") != 0 &&
+ ffStrbufIgnCaseCompS(value, "Not Applicable") != 0 &&
+ ffStrbufIgnCaseCompS(value, "INVALID") != 0 &&
+ ffStrbufIgnCaseCompS(value, "Type1ProductConfigId") != 0 &&
+ ffStrbufIgnCaseCompS(value, "All Series") != 0
+ ;
+}
+
+static void getHostValue(const char* devicesPath, const char* classPath, FFstrbuf* buffer)
+{
+ ffReadFileBuffer(devicesPath, buffer);
+ if(hostValueSet(buffer))
+ return;
+
+ ffReadFileBuffer(classPath, buffer);
+ if(hostValueSet(buffer))
+ return;
+
+ ffStrbufClear(buffer);
+}
+
+void ffDetectChassis(FFChassisResult* result)
+{
+ ffStrbufInit(&result->error);
+
+ ffStrbufInit(&result->chassisType);
+ getHostValue("/sys/devices/virtual/dmi/id/chassis_type", "/sys/class/dmi/id/chassis_type", &result->chassisType);
+
+ ffStrbufInit(&result->chassisVendor);
+ getHostValue("/sys/devices/virtual/dmi/id/chassis_vendor", "/sys/class/dmi/id/chassis_vendor", &result->chassisVendor);
+
+ ffStrbufInit(&result->chassisVersion);
+ getHostValue("/sys/devices/virtual/dmi/id/chassis_version", "/sys/class/dmi/id/chassis_version", &result->chassisVersion);
+}
diff --git a/src/detection/chassis/chassis_nosupport.c b/src/detection/chassis/chassis_nosupport.c
new file mode 100644
index 000000000..42c182567
--- /dev/null
+++ b/src/detection/chassis/chassis_nosupport.c
@@ -0,0 +1,10 @@
+#include "chassis.h"
+
+void ffDetectChassis(FFChassisResult* result)
+{
+ ffStrbufInitS(&result->error, "Not supported on this platform");
+
+ ffStrbufInit(&result->chassisType);
+ ffStrbufInit(&result->chassisVendor);
+ ffStrbufInit(&result->chassisVersion);
+}
diff --git a/src/detection/cpu/cpu.c b/src/detection/cpu/cpu.c
index 3a304606e..62537aaa8 100644
--- a/src/detection/cpu/cpu.c
+++ b/src/detection/cpu/cpu.c
@@ -1,37 +1,15 @@
#include "cpu.h"
-#include "common/caching.h"
#include "detection/internal.h"
-#define FF_CPU_CACHE_NAME "cpu"
-
-void ffDetectCPUImpl(const FFinstance* instance, FFCPUResult* cpu, bool cached);
-
-static bool cacheCallback(FFCPUResult* cpu, FFCache* cache, FFCacheMethodStrbuf strbufMethod, FFCacheMethodData dataMethod)
-{
- return
- strbufMethod(cache, &cpu->vendor) &&
- strbufMethod(cache, &cpu->name) &&
- dataMethod(cache, sizeof(cpu->coresPhysical), &cpu->coresPhysical) &&
- dataMethod(cache, sizeof(cpu->coresLogical), &cpu->coresLogical) &&
- dataMethod(cache, sizeof(cpu->coresOnline), &cpu->coresOnline) &&
- dataMethod(cache, sizeof(cpu->frequencyMin), &cpu->frequencyMin) &&
- dataMethod(cache, sizeof(cpu->frequencyMax), &cpu->frequencyMax);
-}
-
+void ffDetectCPUImpl(const FFinstance* instance, FFCPUResult* cpu);
static void detectCPU(const FFinstance* instance, FFCPUResult* cpu)
{
ffStrbufInit(&cpu->name);
ffStrbufInit(&cpu->vendor);
- bool cached = ffCacheRead(instance, cpu, FF_CPU_CACHE_NAME, (FFCacheMethodCallback) cacheCallback);
-
- ffDetectCPUImpl(instance, cpu, cached);
-
- if(cached)
- return;
+ ffDetectCPUImpl(instance, cpu);
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",
@@ -40,8 +18,6 @@ static void detectCPU(const FFinstance* instance, FFCPUResult* cpu)
ffStrbufRemoveStringsA(&cpu->name, sizeof(removeStrings) / sizeof(removeStrings[0]), removeStrings);
ffStrbufSubstrBeforeFirstC(&cpu->name, '@'); //Cut the speed output in the name as we append our own
ffStrbufTrimRight(&cpu->name, ' '); //If we removed the @ in previous step there was most likely a space before it
-
- ffCacheWrite(instance, cpu, FF_CPU_CACHE_NAME, (FFCacheMethodCallback) cacheCallback);
}
const FFCPUResult* ffDetectCPU(const FFinstance* instance)
diff --git a/src/detection/cpu/cpu_apple.c b/src/detection/cpu/cpu_apple.c
index dc0c1a3eb..3de61f93d 100644
--- a/src/detection/cpu/cpu_apple.c
+++ b/src/detection/cpu/cpu_apple.c
@@ -15,7 +15,7 @@ static double getFrequency(const char* propName)
static double detectCpuTemp(const FFstrbuf* cpuName)
{
- FFlist temps;
+ FF_LIST_AUTO_DESTROY temps;
ffListInit(&temps, sizeof(FFTempValue));
if(ffStrbufStartsWithS(cpuName, "Apple M1"))
@@ -38,36 +38,32 @@ static double detectCpuTemp(const FFstrbuf* cpuName)
ffStrbufDestroy(&tempValue->deviceClass);
}
result /= temps.length;
- ffListDestroy(&temps);
return result;
}
-void ffDetectCPUImpl(const FFinstance* instance, FFCPUResult* cpu, bool cached)
+void ffDetectCPUImpl(const FFinstance* instance, FFCPUResult* cpu)
{
FF_UNUSED(instance);
- if(!cached)
- {
- ffSysctlGetString("machdep.cpu.brand_string", &cpu->name);
- ffSysctlGetString("machdep.cpu.vendor", &cpu->vendor);
+ ffSysctlGetString("machdep.cpu.brand_string", &cpu->name);
+ ffSysctlGetString("machdep.cpu.vendor", &cpu->vendor);
- cpu->coresPhysical = (uint16_t) ffSysctlGetInt("hw.physicalcpu_max", 1);
- if(cpu->coresPhysical == 1)
- cpu->coresPhysical = (uint16_t) ffSysctlGetInt("hw.physicalcpu", 1);
+ cpu->coresPhysical = (uint16_t) ffSysctlGetInt("hw.physicalcpu_max", 1);
+ if(cpu->coresPhysical == 1)
+ cpu->coresPhysical = (uint16_t) ffSysctlGetInt("hw.physicalcpu", 1);
- cpu->coresLogical = (uint16_t) ffSysctlGetInt("hw.logicalcpu_max", 1);
- if(cpu->coresLogical == 1)
- cpu->coresLogical = (uint16_t) ffSysctlGetInt("hw.ncpu", 1);
+ cpu->coresLogical = (uint16_t) ffSysctlGetInt("hw.logicalcpu_max", 1);
+ if(cpu->coresLogical == 1)
+ cpu->coresLogical = (uint16_t) ffSysctlGetInt("hw.ncpu", 1);
- cpu->coresOnline = (uint16_t) ffSysctlGetInt("hw.logicalcpu", 1);
- if(cpu->coresOnline == 1)
- cpu->coresOnline = (uint16_t) ffSysctlGetInt("hw.activecpu", 1);
+ cpu->coresOnline = (uint16_t) ffSysctlGetInt("hw.logicalcpu", 1);
+ if(cpu->coresOnline == 1)
+ cpu->coresOnline = (uint16_t) ffSysctlGetInt("hw.activecpu", 1);
- cpu->frequencyMin = getFrequency("hw.cpufrequency_min");
- cpu->frequencyMax = getFrequency("hw.cpufrequency_max");
- if(cpu->frequencyMax == 0.0)
- cpu->frequencyMax = getFrequency("hw.cpufrequency");
- }
+ cpu->frequencyMin = getFrequency("hw.cpufrequency_min");
+ cpu->frequencyMax = getFrequency("hw.cpufrequency_max");
+ if(cpu->frequencyMax == 0.0)
+ cpu->frequencyMax = getFrequency("hw.cpufrequency");
if (instance->config.cpuTemp)
cpu->temperature = detectCpuTemp(&cpu->name);
diff --git a/src/detection/cpu/cpu_bsd.c b/src/detection/cpu/cpu_bsd.c
index 46f60b4e6..2308c8985 100644
--- a/src/detection/cpu/cpu_bsd.c
+++ b/src/detection/cpu/cpu_bsd.c
@@ -1,14 +1,21 @@
#include "cpu.h"
#include "common/sysctl.h"
-void ffDetectCPUImpl(const FFinstance* instance, FFCPUResult* cpu, bool cached)
+void ffDetectCPUImpl(const FFinstance* instance, FFCPUResult* cpu)
{
FF_UNUSED(instance);
- cpu->temperature = FF_CPU_TEMP_UNSET;
-
- if(cached)
- return;
+ if (instance->config.cpuTemp)
+ {
+ FFstrbuf cpuTemp;
+ ffStrbufInit(&cpuTemp);
+ if(ffSysctlGetString("temperature", &cpuTemp))
+ cpu->temperature = FF_CPU_TEMP_UNSET;
+ else
+ cpu->temperature = ffStrbufToDouble(&cpuTemp);
+ }
+ else
+ cpu->temperature = FF_CPU_TEMP_UNSET;
ffSysctlGetString("hw.model", &cpu->name);
diff --git a/src/detection/cpu/cpu_linux.c b/src/detection/cpu/cpu_linux.c
index 83c0f1140..11b5ec965 100644
--- a/src/detection/cpu/cpu_linux.c
+++ b/src/detection/cpu/cpu_linux.c
@@ -3,10 +3,11 @@
#include "common/properties.h"
#include "detection/temps/temps_linux.h"
+#include
#include
#include
-static void parseCpuInfo(FFCPUResult* cpu, FFstrbuf* physicalCoresBuffer)
+static void parseCpuInfo(FFCPUResult* cpu, FFstrbuf* physicalCoresBuffer, FFstrbuf* cpuMHz)
{
FILE* cpuinfo = fopen("/proc/cpuinfo", "r");
if(cpuinfo == NULL)
@@ -25,6 +26,7 @@ static void parseCpuInfo(FFCPUResult* cpu, FFstrbuf* physicalCoresBuffer)
ffParsePropLine(line, "model name :", &cpu->name) ||
ffParsePropLine(line, "vendor_id :", &cpu->vendor) ||
ffParsePropLine(line, "cpu cores :", physicalCoresBuffer) ||
+ ffParsePropLine(line, "cpu MHz :", cpuMHz) ||
(cpu->name.length == 0 && ffParsePropLine(line, "Hardware :", &cpu->name)) //For Android devices
);
}
@@ -39,16 +41,19 @@ static double getGHz(const char* file)
{
FFstrbuf content;
ffStrbufInit(&content);
- ffReadFileBuffer(file, &content);
- double herz = ffStrbufToDouble(&content);
- ffStrbufDestroy(&content);
+ if(ffAppendFileBuffer(file, &content))
+ {
+ double herz = ffStrbufToDouble(&content);
+ ffStrbufDestroy(&content);
- //ffStrbufToDouble failed
- if(herz != herz)
- return 0;
+ //ffStrbufToDouble failed
+ if(herz != herz)
+ return 0;
- herz /= 1000.0; //to MHz
- return herz / 1000.0; //to GHz
+ herz /= 1000.0; //to MHz
+ return herz / 1000.0; //to GHz
+ }
+ return 0;
}
static double getFrequency(const char* info, const char* scaling)
@@ -78,34 +83,37 @@ static double detectCPUTemp(const FFinstance* instance)
return FF_CPU_TEMP_UNSET;
}
-void ffDetectCPUImpl(const FFinstance* instance, FFCPUResult* cpu, bool cached)
+void ffDetectCPUImpl(const FFinstance* instance, FFCPUResult* cpu)
{
if(instance->config.cpuTemp)
cpu->temperature = detectCPUTemp(instance);
else
cpu->temperature = FF_CPU_TEMP_UNSET;
- if(cached)
- return;
-
FFstrbuf physicalCoresBuffer;
ffStrbufInit(&physicalCoresBuffer);
- parseCpuInfo(cpu, &physicalCoresBuffer);
+ FFstrbuf cpuMHz;
+ ffStrbufInit(&cpuMHz);
+
+ parseCpuInfo(cpu, &physicalCoresBuffer, &cpuMHz);
cpu->coresPhysical = ffStrbufToUInt16(&physicalCoresBuffer, 1);
- #ifdef FF_HAVE_SYSINFO_H
- cpu->coresLogical = (uint16_t) get_nprocs_conf();
- cpu->coresOnline = (uint16_t) get_nprocs();
- #else
- cpu->coresLogical = 1;
- cpu->coresOnline = 1;
- #endif
+ cpu->coresLogical = (uint16_t) get_nprocs_conf();
+ cpu->coresOnline = (uint16_t) get_nprocs();
#define BP "/sys/devices/system/cpu/cpufreq/policy0/"
- cpu->frequencyMin = getFrequency(BP"cpuinfo_min_freq", BP"scaling_min_freq");
- cpu->frequencyMax = getFrequency(BP"cpuinfo_max_freq", BP"scaling_max_freq");
+ if(ffFileExists(BP, S_IFDIR))
+ {
+ cpu->frequencyMin = getFrequency(BP"cpuinfo_min_freq", BP"scaling_min_freq");
+ cpu->frequencyMax = getFrequency(BP"cpuinfo_max_freq", BP"scaling_max_freq");
+ }
+ else
+ {
+ cpu->frequencyMin = cpu->frequencyMax = ffStrbufToDouble(&cpuMHz) / 1000;
+ }
ffStrbufDestroy(&physicalCoresBuffer);
+ ffStrbufDestroy(&cpuMHz);
}
diff --git a/src/detection/cpu/cpu_windows.c b/src/detection/cpu/cpu_windows.c
new file mode 100644
index 000000000..84af8ecfb
--- /dev/null
+++ b/src/detection/cpu/cpu_windows.c
@@ -0,0 +1,49 @@
+#include "cpu.h"
+#include "util/windows/registry.h"
+#include "util/mallocHelper.h"
+
+void ffDetectCPUImpl(const FFinstance* instance, FFCPUResult* cpu)
+{
+ FF_UNUSED(instance);
+
+ cpu->temperature = FF_CPU_TEMP_UNSET;
+ cpu->coresPhysical = cpu->coresLogical = cpu->coresOnline = 0;
+ cpu->frequencyMax = cpu->frequencyMin = 0;
+ ffStrbufInit(&cpu->name);
+ ffStrbufInit(&cpu->vendor);
+
+ {
+ DWORD length = 0;
+ GetLogicalProcessorInformationEx(RelationProcessorCore, NULL, &length);
+ SYSTEM_LOGICAL_PROCESSOR_INFORMATION_EX* FF_AUTO_FREE
+ pLogicalInfo = (SYSTEM_LOGICAL_PROCESSOR_INFORMATION_EX*)malloc(length);
+
+ if(pLogicalInfo && GetLogicalProcessorInformationEx(RelationProcessorCore, pLogicalInfo, &length))
+ {
+ for(
+ SYSTEM_LOGICAL_PROCESSOR_INFORMATION_EX* ptr = pLogicalInfo;
+ (uint8_t*)ptr < ((uint8_t*)pLogicalInfo) + length;
+ ptr = (SYSTEM_LOGICAL_PROCESSOR_INFORMATION_EX*)(((uint8_t*)ptr) + ptr->Size)
+ )
+ {
+ if(ptr->Relationship == RelationProcessorCore)
+ ++cpu->coresPhysical;
+ }
+ }
+ }
+ cpu->coresOnline = (uint16_t)GetActiveProcessorCount(ALL_PROCESSOR_GROUPS);
+ cpu->coresLogical = (uint16_t)GetMaximumProcessorCount(ALL_PROCESSOR_GROUPS);
+
+ FF_HKEY_AUTO_DESTROY hKey;
+ if(!ffRegOpenKeyForRead(HKEY_LOCAL_MACHINE, L"HARDWARE\\DESCRIPTION\\System\\CentralProcessor\\0", &hKey, NULL))
+ return;
+
+ {
+ uint32_t mhz;
+ if(ffRegReadUint(hKey, L"~MHz", &mhz, NULL))
+ cpu->frequencyMax = mhz / 1000.0;
+ }
+
+ ffRegReadStrbuf(hKey, L"ProcessorNameString", &cpu->name, NULL);
+ ffRegReadStrbuf(hKey, L"VendorIdentifier", &cpu->vendor, NULL);
+}
diff --git a/src/detection/cpuUsage/cpuUsage.c b/src/detection/cpuUsage/cpuUsage.c
new file mode 100644
index 000000000..88355668d
--- /dev/null
+++ b/src/detection/cpuUsage/cpuUsage.c
@@ -0,0 +1,44 @@
+#include "fastfetch.h"
+#include "cpuUsage.h"
+
+#include "common/time.h"
+
+#include
+
+// We need to use uint64_t because sizeof(long) == 4 on Windows
+const char* ffGetCpuUsageInfo(uint64_t* inUseAll, uint64_t* totalAll);
+
+static uint64_t inUseAll1, totalAll1;
+
+void ffPrepareCPUUsage()
+{
+ ffGetCpuUsageInfo(&inUseAll1, &totalAll1);
+}
+
+const char* ffGetCpuUsageResult(double* result)
+{
+ const char* error = NULL;
+ if(inUseAll1 == 0 && totalAll1 == 0)
+ {
+ error = ffGetCpuUsageInfo(&inUseAll1, &totalAll1);
+ if(error)
+ return error;
+ ffTimeSleep(200);
+ }
+
+ while(true)
+ {
+ uint64_t inUseAll2, totalAll2;
+ error = ffGetCpuUsageInfo(&inUseAll2, &totalAll2);
+ if(error)
+ return error;
+
+ if(inUseAll2 != inUseAll1)
+ {
+ *result = (double)(inUseAll2 - inUseAll1) / (double)(totalAll2 - totalAll1) * 100;
+ return NULL;
+ }
+ else
+ ffTimeSleep(200);
+ }
+}
diff --git a/src/detection/cpuUsage/cpuUsage.h b/src/detection/cpuUsage/cpuUsage.h
index cddd1866b..250b30570 100644
--- a/src/detection/cpuUsage/cpuUsage.h
+++ b/src/detection/cpuUsage/cpuUsage.h
@@ -3,6 +3,6 @@
#ifndef FF_INCLUDED_detection_cpu_cpuUsage
#define FF_INCLUDED_detection_cpu_cpuUsage
-const char* ffGetCpuUsageInfo(long* inUseAll, long* totalAll);
+const char* ffGetCpuUsageResult(double* result);
#endif
diff --git a/src/detection/cpuUsage/cpuUsage_apple.c b/src/detection/cpuUsage/cpuUsage_apple.c
index e170a7ceb..38a6b4865 100644
--- a/src/detection/cpuUsage/cpuUsage_apple.c
+++ b/src/detection/cpuUsage/cpuUsage_apple.c
@@ -4,7 +4,7 @@
#include
#include
-const char* ffGetCpuUsageInfo(long* inUseAll, long* totalAll)
+const char* ffGetCpuUsageInfo(uint64_t* inUseAll, uint64_t* totalAll)
{
natural_t numCPUs = 0U;
processor_info_array_t cpuInfo;
@@ -21,8 +21,8 @@ const char* ffGetCpuUsageInfo(long* inUseAll, long* totalAll)
+ cpuInfo[CPU_STATE_MAX * i + CPU_STATE_SYSTEM]
+ cpuInfo[CPU_STATE_MAX * i + CPU_STATE_NICE];
integer_t total = inUse + cpuInfo[CPU_STATE_MAX * i + CPU_STATE_IDLE];
- *inUseAll += inUse;
- *totalAll += total;
+ *inUseAll += (uint64_t)inUse;
+ *totalAll += (uint64_t)total;
}
return NULL;
}
diff --git a/src/detection/cpuUsage/cpuUsage_bsd.c b/src/detection/cpuUsage/cpuUsage_bsd.c
new file mode 100644
index 000000000..d96064fd4
--- /dev/null
+++ b/src/detection/cpuUsage/cpuUsage_bsd.c
@@ -0,0 +1,19 @@
+#include "cpuUsage.h"
+
+#include
+#include
+#include
+
+const char* ffGetCpuUsageInfo(uint64_t* inUseAll, uint64_t* totalAll)
+{
+ // interrupt processing, user processes, system processing, lock spinning, and idling
+ uint64_t cpTime[5];
+ size_t neededLength = sizeof(cpTime);
+ if(sysctlbyname("kern.cp_time", cpTime, &neededLength, NULL, 0) != 0)
+ return "sysctlbyname(kern.cp_time) failed";
+
+ *inUseAll = cpTime[0] + cpTime[1] + cpTime[2] + cpTime[3];
+ *totalAll = *inUseAll + cpTime[4];
+
+ return NULL;
+}
diff --git a/src/detection/cpuUsage/cpuUsage_linux.c b/src/detection/cpuUsage/cpuUsage_linux.c
index 3696a8d77..1888b2b13 100644
--- a/src/detection/cpuUsage/cpuUsage_linux.c
+++ b/src/detection/cpuUsage/cpuUsage_linux.c
@@ -2,16 +2,23 @@
#include "cpuUsage.h"
#include
+#include
-const char* ffGetCpuUsageInfo(long* inUseAll, long* totalAll)
+const char* ffGetCpuUsageInfo(uint64_t* inUseAll, uint64_t* totalAll)
{
- long user, nice, system, idle, iowait, irq, softirq;
+ uint64_t user = 0, nice = 0, system = 0, idle = 0, iowait = 0, irq = 0, softirq = 0;
FILE* procStat = fopen("/proc/stat", "r");
if(procStat == NULL)
+ {
+ #ifdef __ANDROID__
+ return "Accessing \"/proc/stat\" is restricted on Android O+";
+ #else
return "fopen(\"""/proc/stat\", \"r\") == NULL";
+ #endif
+ }
- if (fscanf(procStat, "cpu%ld%ld%ld%ld%ld%ld%ld", &user, &nice, &system, &idle, &iowait, &irq, &softirq) < 0)
+ if (fscanf(procStat, "cpu%" PRIu64 "%" PRIu64 "%" PRIu64 "%" PRIu64 "%" PRIu64 "%" PRIu64 "%" PRIu64, &user, &nice, &system, &idle, &iowait, &irq, &softirq) < 0)
{
fclose(procStat);
return "fscanf() failed";
diff --git a/src/detection/cpuUsage/cpuUsage_windows.c b/src/detection/cpuUsage/cpuUsage_windows.c
new file mode 100644
index 000000000..d4ffbd2ca
--- /dev/null
+++ b/src/detection/cpuUsage/cpuUsage_windows.c
@@ -0,0 +1,62 @@
+#include "fastfetch.h"
+#include "cpuUsage.h"
+
+#ifdef FF_USE_WIN_NTAPI
+
+#include "util/mallocHelper.h"
+
+#include
+#include
+
+const char* ffGetCpuUsageInfo(uint64_t* inUseAll, uint64_t* totalAll)
+{
+ ULONG size = 0;
+ if(NtQuerySystemInformation(SystemProcessorPerformanceInformation, NULL, 0, &size) != STATUS_INFO_LENGTH_MISMATCH)
+ return "NtQuerySystemInformation(SystemProcessorPerformanceInformation, NULL) failed";
+
+ SYSTEM_PROCESSOR_PERFORMANCE_INFORMATION* FF_AUTO_FREE pinfo = (SYSTEM_PROCESSOR_PERFORMANCE_INFORMATION*)malloc(size);
+ if(!NT_SUCCESS(NtQuerySystemInformation(SystemProcessorPerformanceInformation, pinfo, size, &size)))
+ return "NtQuerySystemInformation(SystemProcessorPerformanceInformation, size) failed";
+
+ *inUseAll = *totalAll = 0;
+
+ for (uint32_t i = 0; i < size / sizeof(SYSTEM_PROCESSOR_PERFORMANCE_INFORMATION); ++i)
+ {
+ SYSTEM_PROCESSOR_PERFORMANCE_INFORMATION* coreInfo = pinfo + i;
+
+ // KernelTime includes idle time.
+ LONGLONG dpcTime = coreInfo->Reserved1[0].QuadPart;
+ LONGLONG interruptTime = coreInfo->Reserved1[1].QuadPart;
+ coreInfo->KernelTime.QuadPart -= coreInfo->IdleTime.QuadPart;
+ coreInfo->KernelTime.QuadPart += dpcTime + interruptTime;
+
+ LONGLONG inUse = coreInfo->UserTime.QuadPart + coreInfo->KernelTime.QuadPart;
+ *inUseAll += (uint64_t)inUse;
+ *totalAll += (uint64_t)(inUse + coreInfo->IdleTime.QuadPart);
+ }
+
+ return NULL;
+}
+
+#else
+
+#include
+
+static inline uint64_t fileTimeToUint64(const FILETIME* ft) {
+ return (((uint64_t)ft->dwHighDateTime) << 32) | ((uint64_t)ft->dwLowDateTime);
+}
+
+const char* ffGetCpuUsageInfo(uint64_t* inUseAll, uint64_t* totalAll)
+{
+ FILETIME idleTime, kernelTime, userTime;
+ if(!GetSystemTimes(&idleTime, &kernelTime, &userTime))
+ return "GetSystemTimes() failed";
+
+ // https://learn.microsoft.com/en-us/windows/win32/api/processthreadsapi/nf-processthreadsapi-getsystemtimes
+ // `kernelTime` also includes the amount of time the system has been idle.
+ *totalAll = fileTimeToUint64(&userTime) + fileTimeToUint64(&kernelTime);
+ *inUseAll = *totalAll - fileTimeToUint64(&idleTime);
+ return NULL;
+}
+
+#endif
diff --git a/src/detection/cursor/cursor.h b/src/detection/cursor/cursor.h
new file mode 100644
index 000000000..268631240
--- /dev/null
+++ b/src/detection/cursor/cursor.h
@@ -0,0 +1,17 @@
+#pragma once
+
+#ifndef FF_INCLUDED_detection_cursor_cursor
+#define FF_INCLUDED_detection_cursor_cursor
+
+#include "fastfetch.h"
+
+typedef struct FFCursorResult
+{
+ FFstrbuf theme;
+ FFstrbuf size;
+ FFstrbuf error;
+} FFCursorResult;
+
+void ffDetectCursor(const FFinstance* instance, FFCursorResult* result);
+
+#endif
diff --git a/src/detection/cursor/cursor_linux.c b/src/detection/cursor/cursor_linux.c
new file mode 100644
index 000000000..a8dfb8810
--- /dev/null
+++ b/src/detection/cursor/cursor_linux.c
@@ -0,0 +1,131 @@
+#include "cursor.h"
+
+#include "common/properties.h"
+#include "common/parsing.h"
+#include "common/settings.h"
+#include "detection/gtk.h"
+#include "detection/displayserver/displayserver.h"
+
+#include
+
+static void detectCursorGTK(const FFinstance* instance, FFCursorResult* result)
+{
+ const FFGTKResult* gtk = ffDetectGTK4(instance);
+
+ if(gtk->cursor.length == 0)
+ gtk = ffDetectGTK3(instance);
+
+ if(gtk->cursor.length == 0)
+ gtk = ffDetectGTK2(instance);
+
+ if(gtk->cursor.length == 0)
+ {
+ ffStrbufAppendS(&result->error, "Couldn't detect GTK Cursor");
+ return;
+ }
+
+ ffStrbufAppend(&result->theme, >k->cursor);
+ ffStrbufAppend(&result->size, >k->cursorSize);
+}
+
+static void detectCursorXFCE(const FFinstance* instance, FFCursorResult* result)
+{
+ ffStrbufAppendS(&result->theme, ffSettingsGetXFConf(instance, "xsettings", "/Gtk/CursorThemeName", FF_VARIANT_TYPE_STRING).strValue);
+
+ if(result->theme.length == 0)
+ ffStrbufAppendS(&result->error, "Couldn't find xfce cursor in xfconf (xsettings::/Gtk/CursorThemeName)");
+
+ int cursorSizeVal = ffSettingsGetXFConf(instance, "xsettings", "/Gtk/CursorThemeSize", FF_VARIANT_TYPE_INT).intValue;
+ if(cursorSizeVal > 0)
+ ffStrbufAppendF(&result->size, "%i", cursorSizeVal);
+}
+
+static void detectCursorFromConfigFile(const FFinstance* instance, const char* relativeFilePath, const char* themeStart, const char* themeDefault, const char* sizeStart, const char* sizeDefault, FFCursorResult* result)
+{
+ if(ffParsePropFileConfigValues(instance, relativeFilePath, 2, (FFpropquery[]) {
+ {themeStart, &result->theme},
+ {sizeStart, &result->size}
+ })) {
+
+ if(result->theme.length == 0)
+ ffStrbufAppendS(&result->theme, themeDefault);
+
+ if(result->size.length == 0)
+ ffStrbufAppendS(&result->size, sizeDefault);
+ }
+
+ if(result->theme.length == 0)
+ ffStrbufAppendF(&result->error, "Couldn't find cursor in %s", relativeFilePath);
+}
+
+static bool detectCursorFromXResources(const FFinstance* instance, FFCursorResult* result)
+{
+ ffParsePropFileHomeValues(instance, ".Xresources", 2, (FFpropquery[]) {
+ {"Xcursor.theme :", &result->theme},
+ {"Xcursor.size :", &result->size}
+ });
+
+ return result->theme.length > 0;
+}
+
+static bool detectCursorFromXDG(const FFinstance* instance, bool user, FFCursorResult* result)
+{
+ if(user)
+ ffParsePropFileHome(instance, ".icons/default/index.theme", "Inherits =", &result->theme);
+ else
+ ffParsePropFile(FASTFETCH_TARGET_DIR_USR"/share/icons/default/index.theme", "Inherits =", &result->theme);
+
+ return result->theme.length > 0;
+}
+
+static bool detectCursorFromEnv(const FFinstance* instance, FFCursorResult* result)
+{
+ FF_UNUSED(instance);
+ const char* xcursor_theme = getenv("XCURSOR_THEME");
+
+ if(!ffStrSet(xcursor_theme))
+ return false;
+
+ ffStrbufAppendS(&result->theme, xcursor_theme);
+ ffStrbufAppendS(&result->size, getenv("XCURSOR_SIZE"));
+
+ return true;
+}
+
+void ffDetectCursor(const FFinstance* instance, FFCursorResult* result)
+{
+ const FFDisplayServerResult* wmde = ffConnectDisplayServer(instance);
+
+ if(ffStrbufCompS(&wmde->wmPrettyName, "WSLg") == 0)
+ {
+ ffStrbufAppendS(&result->error, "WSLg uses native windows cursor");
+ return;
+ }
+
+ if(ffStrbufIgnCaseCompS(&wmde->wmProtocolName, "TTY") == 0)
+ {
+ ffStrbufAppendS(&result->error, "Cursor isn't supported in TTY");
+ return;
+ }
+
+ if(ffStrbufIgnCaseCompS(&wmde->dePrettyName, "KDE Plasma") == 0)
+ return detectCursorFromConfigFile(instance, "kcminputrc", "cursorTheme =", "Breeze", "cursorSize =", "24", result);
+
+ if(ffStrbufStartsWithIgnCaseS(&wmde->dePrettyName, "XFCE"))
+ return detectCursorXFCE(instance, result);
+
+ if(ffStrbufStartsWithIgnCaseS(&wmde->dePrettyName, "LXQt"))
+ return detectCursorFromConfigFile(instance, "lxqt/session.conf", "cursor_theme =", "Adwaita", "cursor_size =", "24", result);
+
+ if(ffStrbufIgnCaseCompS(&wmde->dePrettyName, "Gnome") == 0 || ffStrbufIgnCaseCompS(&wmde->dePrettyName, "Cinnamon") == 0 || ffStrbufIgnCaseCompS(&wmde->dePrettyName, "Mate") == 0)
+ return detectCursorGTK(instance, result);
+
+ if(
+ detectCursorFromEnv(instance, result) ||
+ detectCursorFromXDG(instance, true, result) ||
+ detectCursorFromXResources(instance, result) ||
+ detectCursorFromXDG(instance, false, result)
+ ) return;
+
+ detectCursorGTK(instance, result);
+}
diff --git a/src/detection/cursor/cursor_nosupport.c b/src/detection/cursor/cursor_nosupport.c
new file mode 100644
index 000000000..c54637c96
--- /dev/null
+++ b/src/detection/cursor/cursor_nosupport.c
@@ -0,0 +1,7 @@
+#include "cursor.h"
+
+void ffDetectCursor(const FFinstance* instance, FFCursorResult* result)
+{
+ FF_UNUSED(instance, result);
+ ffStrbufInitS(&result->error, "Not supported on this platform");
+}
diff --git a/src/detection/cursor/cursor_windows.c b/src/detection/cursor/cursor_windows.c
new file mode 100644
index 000000000..b231d85b2
--- /dev/null
+++ b/src/detection/cursor/cursor_windows.c
@@ -0,0 +1,12 @@
+#include "cursor.h"
+
+#include "util/windows/registry.h"
+
+void ffDetectCursor(const FFinstance* instance, FFCursorResult* result)
+{
+ FF_UNUSED(instance);
+
+ FF_HKEY_AUTO_DESTROY hKey;
+ if(ffRegOpenKeyForRead(HKEY_CURRENT_USER, L"Control Panel\\Cursors", &hKey, &result->error))
+ ffRegReadStrbuf(hKey, NULL, &result->theme, &result->error);
+}
diff --git a/src/detection/datetime.c b/src/detection/datetime.c
index 9c5ebf414..20b8ec367 100644
--- a/src/detection/datetime.c
+++ b/src/detection/datetime.c
@@ -1,21 +1,21 @@
#include "fastfetch.h"
#include "detection/datetime.h"
+#include "common/thread.h"
#include
-#include
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 FFThreadMutex mutex = FF_THREAD_MUTEX_INITIALIZER;
static bool init = false;
- pthread_mutex_lock(&mutex);
+ ffThreadMutexLock(&mutex);
if (init)
{
- pthread_mutex_unlock(&mutex);
+ ffThreadMutexUnlock(&mutex);
return &result;
}
init = true;
@@ -68,6 +68,6 @@ const FFDateTimeResult* ffDetectDateTime(const FFinstance* instance)
ffStrbufInitA(&result.secondPretty, FASTFETCH_STRBUF_DEFAULT_ALLOC);
result.secondPretty.length = (uint32_t) strftime(result.secondPretty.chars, ffStrbufGetFree(&result.secondPretty), "%S", tm);
- pthread_mutex_unlock(&mutex);
+ ffThreadMutexUnlock(&mutex);
return &result;
}
diff --git a/src/detection/disk/disk.c b/src/detection/disk/disk.c
new file mode 100644
index 000000000..de25d634e
--- /dev/null
+++ b/src/detection/disk/disk.c
@@ -0,0 +1,28 @@
+#include "disk.h"
+#include "detection/internal.h"
+
+void ffDetectDisksImpl(FFDiskResult* disks);
+
+static int compareDisks(const void* disk1, const void* disk2)
+{
+ return ffStrbufCompAlphabetically(&((const FFDisk*) disk1)->mountpoint, &((const FFDisk*) disk2)->mountpoint);
+}
+
+const FFDiskResult* ffDetectDisks()
+{
+ FF_DETECTION_INTERNAL_GUARD(FFDiskResult,
+ ffStrbufInit(&result.error);
+ ffListInitA(&result.disks, sizeof(FFDisk), 4);
+
+ ffDetectDisksImpl(&result);
+
+ if(result.disks.length == 0 && result.error.length == 0)
+ ffStrbufAppendS(&result.error, "No disks found");
+
+ //We need to sort the disks, so that we can detect, which disk a path resides on
+ // For example for /boot/efi/bootmgr we need to check /boot/efi before /boot
+ //Note that we sort alphabetically here for a better ordering when printing the list,
+ // so the check must be done in reverse order
+ ffListSort(&result.disks, compareDisks);
+ );
+}
diff --git a/src/detection/disk/disk.h b/src/detection/disk/disk.h
index aaf34a608..c061ce6c9 100644
--- a/src/detection/disk/disk.h
+++ b/src/detection/disk/disk.h
@@ -5,6 +5,39 @@
#include "fastfetch.h"
-const char* ffDiskAutodetectFolders(FFinstance* instance, FFlist* folders);
+typedef enum FFDiskType
+{
+ FF_DISK_TYPE_REGULAR,
+ FF_DISK_TYPE_HIDDEN,
+ FF_DISK_TYPE_EXTERNAL
+} FFDiskType;
+
+typedef struct FFDisk
+{
+ FFstrbuf mountpoint;
+ FFstrbuf filesystem;
+ FFstrbuf name;
+ FFDiskType type;
+
+ uint64_t bytesUsed;
+ uint64_t bytesTotal;
+
+ uint32_t filesUsed;
+ uint32_t filesTotal;
+} FFDisk;
+
+typedef struct FFDiskResult
+{
+ FFstrbuf error;
+ FFlist disks; //List of FFDisk
+} FFDiskResult;
+
+/**
+ * Returns a List of FFDisk, sorted alphabetically by mountpoint.
+ * If error is not set, disks contains at least one disk.
+ *
+ * @return const FFDiskResult*
+ */
+const FFDiskResult* ffDetectDisks();
#endif
diff --git a/src/detection/disk/disk_apple.m b/src/detection/disk/disk_apple.m
index 95d5fd8ec..b2f7cec08 100644
--- a/src/detection/disk/disk_apple.m
+++ b/src/detection/disk/disk_apple.m
@@ -1,25 +1,17 @@
#include "disk.h"
+#include
#import
-const char* ffDiskAutodetectFolders(FFinstance* instance, FFlist* folders)
+void detectFsInfo(struct statfs* fs, FFDisk* disk)
{
- NSArray *keys = [NSArray arrayWithObjects:NSURLVolumeNameKey, nil];
- NSArray *urls = [NSFileManager.defaultManager mountedVolumeURLsIncludingResourceValuesForKeys:keys
- options:NSVolumeEnumerationSkipHiddenVolumes];
- if(urls == nil)
- return "[NSFileManager.defaultManager mountedVolumeURLsIncludingResourceValuesForKeys] failed";
+ // FreeBSD doesn't support these flags
+ if(fs->f_flags & MNT_DONTBROWSE)
+ disk->type = FF_DISK_TYPE_HIDDEN;
+ else if(fs->f_flags & MNT_REMOVABLE)
+ disk->type = FF_DISK_TYPE_EXTERNAL;
+ else
+ disk->type = FF_DISK_TYPE_REGULAR;
- for (NSURL *url in urls) {
- NSError *error;
- NSNumber* removable;
- if([url getResourceValue:&removable forKey:NSURLVolumeIsRemovableKey error:&error] == NO)
- continue;
- if(removable.boolValue && !instance->config.diskRemovable)
- continue;
-
- ffStrbufInitS((FFstrbuf *)ffListAdd(folders), [url.relativePath cStringUsingEncoding:NSUTF8StringEncoding]);
- }
-
- return NULL;
+ ffStrbufInitS(&disk->name, [NSFileManager.defaultManager displayNameAtPath:@(fs->f_mntonname)].UTF8String);
}
diff --git a/src/detection/disk/disk_bsd.c b/src/detection/disk/disk_bsd.c
new file mode 100644
index 000000000..0e14ef3f2
--- /dev/null
+++ b/src/detection/disk/disk_bsd.c
@@ -0,0 +1,56 @@
+#include "disk.h"
+
+#include
+
+#ifdef __FreeBSD__
+static void detectFsInfo(struct statfs* fs, FFDisk* disk)
+{
+ if(
+ ffStrbufStartsWithS(&disk->mountpoint, "/boot") ||
+ ffStrbufStartsWithS(&disk->mountpoint, "/dev") ||
+ ffStrbufStartsWithS(&disk->mountpoint, "/var") ||
+ ffStrbufStartsWithS(&disk->mountpoint, "/tmp") ||
+ ffStrbufStartsWithS(&disk->mountpoint, "/proc") ||
+ ffStrbufStartsWithS(&disk->mountpoint, "/zroot")
+ )
+ disk->type = FF_DISK_TYPE_HIDDEN;
+ else if((fs->f_flags & MNT_NOSUID) || !(fs->f_flags & MNT_LOCAL))
+ disk->type = FF_DISK_TYPE_EXTERNAL;
+ else
+ disk->type = FF_DISK_TYPE_REGULAR;
+
+ ffStrbufInit(&disk->name);
+}
+#else
+void detectFsInfo(struct statfs* fs, FFDisk* disk);
+#endif
+
+void ffDetectDisksImpl(FFDiskResult* disks)
+{
+ struct statfs* buf;
+
+ int size = getmntinfo(&buf, MNT_WAIT);
+ if(size <= 0)
+ ffStrbufAppendS(&disks->error, "getmntinfo() failed");
+
+ for(struct statfs* fs = buf; fs < buf + size; ++fs)
+ {
+ FFDisk* disk = ffListAdd(&disks->disks);
+
+ #ifdef __FreeBSD__
+ // f_bavail and f_ffree are signed on FreeBSD...
+ if(fs->f_bavail < 0) fs->f_bavail = 0;
+ if(fs->f_ffree < 0) fs->f_ffree = 0;
+ #endif
+
+ disk->bytesTotal = fs->f_blocks * fs->f_bsize;
+ disk->bytesUsed = disk->bytesTotal - ((uint64_t)fs->f_bavail * fs->f_bsize);
+
+ disk->filesTotal = (uint32_t) fs->f_files;
+ disk->filesUsed = (uint32_t) (disk->filesTotal - (uint64_t)fs->f_ffree);
+
+ ffStrbufInitS(&disk->mountpoint, fs->f_mntonname);
+ ffStrbufInitS(&disk->filesystem, fs->f_fstypename);
+ detectFsInfo(fs, disk);
+ }
+}
diff --git a/src/detection/disk/disk_linux.c b/src/detection/disk/disk_linux.c
index 80d5808c4..8abfbdf20 100644
--- a/src/detection/disk/disk_linux.c
+++ b/src/detection/disk/disk_linux.c
@@ -1,22 +1,99 @@
#include "disk.h"
+#include
#include
-const char* ffDiskAutodetectFolders(FFinstance* instance, FFlist* folders)
+void ffDetectDisksImpl(FFDiskResult* disks)
{
- FF_UNUSED(instance);
+ FILE* mountsFile = fopen("/proc/mounts", "r");
+ if(mountsFile == NULL)
+ {
+ ffStrbufAppendS(&disks->error, "fopen(\"/proc/mounts\", \"r\") == NULL");
+ return;
+ }
- struct statvfs fsRoot;
- int rootRet = statvfs(FASTFETCH_TARGET_DIR_ROOT"/", &fsRoot);
- if(rootRet != 0)
- return "statvfs(\"/\") failed";
+ char* line = NULL;
+ size_t len = 0;
- ffStrbufInitS((FFstrbuf *)ffListAdd(folders), FASTFETCH_TARGET_DIR_ROOT"/");
+ while(getline(&line, &len, mountsFile) != EOF)
+ {
+ //Format of the file: " ..." (Same as fstab)
+ char* currentPos = line;
- struct statvfs fsHome;
- int homeRet = statvfs(FASTFETCH_TARGET_DIR_HOME, &fsHome);
- if(homeRet == 0 && (fsRoot.f_fsid != fsHome.f_fsid))
- ffStrbufInitS((FFstrbuf *)ffListAdd(folders), FASTFETCH_TARGET_DIR_HOME);
+ //Non pseudo filesystems have their device in /dev/
+ //DrvFs is a filesystem plugin to WSL that was designed to support interop between WSL and the Windows filesystem.
+ if(strncmp(currentPos, "/dev/", 5) != 0 && strncmp(currentPos, "drvfs", 5) != 0)
+ continue;
- return NULL;
+ //Skip /dev/
+ currentPos += 5;
+
+ //Don't show loop file systems
+ if(strncasecmp(currentPos, "loop", 4) == 0)
+ continue;
+
+ FFDisk* disk = ffListAdd(&disks->disks);
+
+ //Go to mountpoint
+ while(!isspace(*currentPos) && *currentPos != '\0')
+ ++currentPos;
+ while(isspace(*currentPos))
+ ++currentPos;
+
+ ffStrbufInitA(&disk->mountpoint, 16);
+ ffStrbufAppendSUntilC(&disk->mountpoint, currentPos, ' ');
+
+ //Go to filesystem
+ currentPos += disk->mountpoint.length;
+ while(isspace(*currentPos))
+ ++currentPos;
+
+ ffStrbufInitA(&disk->filesystem, 16);
+ ffStrbufAppendSUntilC(&disk->filesystem, currentPos, ' ');
+
+ //Go to options, detect type
+ currentPos += disk->filesystem.length;
+ while(isspace(*currentPos))
+ ++currentPos;
+
+ #ifdef __ANDROID__
+ if(ffStrbufEqualS(&disk->mountpoint, "/") || ffStrbufEqualS(&disk->mountpoint, "/storage/emulated"))
+ disk->type = FF_DISK_TYPE_REGULAR;
+ else if(ffStrbufStartsWithS(&disk->mountpoint, "/mnt/media_rw/"))
+ disk->type = FF_DISK_TYPE_EXTERNAL;
+ else
+ disk->type = FF_DISK_TYPE_HIDDEN;
+ #else
+ if(strstr(currentPos, "nosuid") != NULL || strstr(currentPos, "nodev") != NULL)
+ disk->type = FF_DISK_TYPE_EXTERNAL;
+ else if(ffStrbufStartsWithS(&disk->mountpoint, "/boot") || ffStrbufStartsWithS(&disk->mountpoint, "/efi"))
+ disk->type = FF_DISK_TYPE_HIDDEN;
+ else
+ disk->type = FF_DISK_TYPE_REGULAR;
+ #endif
+
+ //Detects stats
+ #ifdef __USE_LARGEFILE64
+ struct statvfs64 fs;
+ if(statvfs64(disk->mountpoint.chars, &fs) != 0)
+ memset(&fs, 0, sizeof(struct statvfs64)); //Set all values to 0, so our values get initialized to 0 too
+ #else
+ struct statvfs fs;
+ if(statvfs(disk->mountpoint.chars, &fs) != 0)
+ memset(&fs, 0, sizeof(struct statvfs)); //Set all values to 0, so our values get initialized to 0 too
+ #endif
+
+ disk->bytesTotal = fs.f_blocks * fs.f_frsize;
+ disk->bytesUsed = disk->bytesTotal - (fs.f_bavail * fs.f_frsize);
+
+ disk->filesTotal = (uint32_t) fs.f_files;
+ disk->filesUsed = (uint32_t) (disk->filesTotal - fs.f_ffree);
+
+ ffStrbufInit(&disk->name); //TODO: implement this
+ }
+
+ if(line != NULL)
+ free(line);
+
+ fclose(mountsFile);
}
diff --git a/src/detection/disk/disk_windows.c b/src/detection/disk/disk_windows.c
new file mode 100644
index 000000000..2d11739b4
--- /dev/null
+++ b/src/detection/disk/disk_windows.c
@@ -0,0 +1,70 @@
+#include "disk.h"
+
+#define WIN32_LEAN_AND_MEAN 1
+#include
+
+void ffDetectDisksImpl(FFDiskResult* disks)
+{
+ uint32_t length = GetLogicalDriveStringsA(0, NULL);
+ if(length == 0)
+ {
+ ffStrbufAppendS(&disks->error, "GetLogicalDriveStringsA failed");
+ return;
+ }
+
+ char* buf = malloc(length + 1);
+ GetLogicalDriveStringsA(length, buf);
+
+ for(uint32_t i = 0; i < length; i++)
+ {
+ const char* mountpoint = buf + i;
+
+ UINT driveType = GetDriveTypeA(mountpoint);
+ if(driveType == DRIVE_NO_ROOT_DIR)
+ {
+ i += (uint32_t)strlen(mountpoint);
+ continue;
+ }
+
+ FFDisk* disk = ffListAdd(&disks->disks);
+ ffStrbufInitS(&disk->mountpoint, mountpoint);
+
+ uint64_t bytesFree;
+ if(!GetDiskFreeSpaceExA(mountpoint, NULL, (PULARGE_INTEGER)&disk->bytesTotal, (PULARGE_INTEGER)&bytesFree))
+ {
+ disk->bytesTotal = 0;
+ bytesFree = 0;
+ }
+ disk->bytesUsed = disk->bytesTotal - bytesFree;
+
+ if(driveType == DRIVE_REMOVABLE || driveType == DRIVE_REMOTE || driveType == DRIVE_CDROM)
+ disk->type = FF_DISK_TYPE_EXTERNAL;
+ else if(driveType == DRIVE_FIXED)
+ disk->type = FF_DISK_TYPE_REGULAR;
+ else
+ disk->type = FF_DISK_TYPE_HIDDEN;
+
+ ffStrbufInitA(&disk->filesystem, MAX_PATH + 1);
+ ffStrbufInitA(&disk->name, MAX_PATH + 1);
+ //https://learn.microsoft.com/en-us/windows/win32/api/fileapi/nf-fileapi-getvolumeinformationa#remarks
+ UINT errorMode = SetErrorMode(SEM_FAILCRITICALERRORS);
+ GetVolumeInformationA(mountpoint,
+ disk->name.chars, disk->name.allocated, //Volume name
+ NULL, //Serial number
+ NULL, //Max component length
+ NULL, //File system flags
+ disk->filesystem.chars, disk->filesystem.allocated
+ );
+ SetErrorMode(errorMode);
+ ffStrbufRecalculateLength(&disk->name);
+ ffStrbufRecalculateLength(&disk->filesystem);
+
+ //TODO: implement
+ disk->filesUsed = 0;
+ disk->filesTotal = 0;
+
+ i += disk->mountpoint.length;
+ }
+
+ free(buf);
+}
diff --git a/src/detection/displayserver/displayserver_apple.c b/src/detection/displayserver/displayserver_apple.c
index 80211fb38..4d0d802c2 100644
--- a/src/detection/displayserver/displayserver_apple.c
+++ b/src/detection/displayserver/displayserver_apple.c
@@ -4,56 +4,34 @@
#include
#include
#include
-#include
-
-//Resolution code heavily inspired by displayplacer <3
-
-typedef union
-{
- uint8_t rawData[0xDC];
- struct
- {
- uint32_t mode;
- uint32_t flags; // 0x4
- uint32_t width; // 0x8
- uint32_t height; // 0xC
- uint32_t depth; // 0x10
- uint32_t dc2[42];
- uint16_t dc3;
- uint16_t freq; // 0xBC
- uint32_t dc4[4];
- float density; // 0xD0
- } derived;
-} modes_D4;
-
-void CGSGetCurrentDisplayMode(CGDirectDisplayID display, int* modeNum);
-void CGSGetDisplayModeDescriptionOfLength(CGDirectDisplayID display, int idx, modes_D4* mode, int length);
+#include
+#include
static void detectResolution(FFDisplayServerResult* ds)
{
- CGDisplayCount screenCount;
- CGGetOnlineDisplayList(INT_MAX, NULL, &screenCount);
- if(screenCount == 0)
+ CGDirectDisplayID screens[128];
+ uint32_t screenCount;
+ if(CGGetOnlineDisplayList(sizeof(screens) / sizeof(screens[0]), screens, &screenCount) != kCGErrorSuccess)
return;
- CGDirectDisplayID* screens = malloc(screenCount * sizeof(CGDirectDisplayID));
- CGGetOnlineDisplayList(INT_MAX, screens, &screenCount);
-
for(uint32_t i = 0; i < screenCount; i++)
{
- int modeID;
- CGSGetCurrentDisplayMode(screens[i], &modeID);
- modes_D4 mode;
- CGSGetDisplayModeDescriptionOfLength(screens[i], modeID, &mode, 0xD4);
-
- uint32_t refreshRate = ffdsParseRefreshRate(mode.derived.freq);
- ffdsAppendResolution(ds, mode.derived.width, mode.derived.height, refreshRate);
+ CGDirectDisplayID screen = screens[i];
+ CGDisplayModeRef mode = CGDisplayCopyDisplayMode(screen);
+ if(mode)
+ {
+ ffdsAppendResolution(ds,
+ (uint32_t)CGDisplayModeGetWidth(mode),
+ (uint32_t)CGDisplayModeGetHeight(mode),
+ (uint32_t)CGDisplayModeGetRefreshRate(mode)
+ );
+ CGDisplayModeRelease(mode);
+ }
+ CGDisplayRelease(screen);
}
-
- free(screens);
}
-static void detectWM(FFDisplayServerResult* ds)
+static void detectWMPlugin(FFstrbuf* name)
{
int request[] = {CTL_KERN, KERN_PROC, KERN_PROC_ALL};
u_int requestLength = sizeof(request) / sizeof(*request);
@@ -76,9 +54,8 @@ static void detectWM(FFDisplayServerResult* ds)
strcasecmp(comm, "rectangle") != 0
) continue;
- ffStrbufAppendS(&ds->wmProcessName, comm);
- ffStrbufAppendS(&ds->wmPrettyName, comm);
- ds->wmPrettyName.chars[0] = (char) toupper(ds->wmPrettyName.chars[0]);
+ ffStrbufAppendS(name, comm);
+ name->chars[0] = (char) toupper(name->chars[0]);
break;
}
@@ -89,19 +66,21 @@ void ffConnectDisplayServerImpl(FFDisplayServerResult* ds, const FFinstance* ins
{
FF_UNUSED(instance);
- ffStrbufInit(&ds->wmProcessName);
- ffStrbufInit(&ds->wmPrettyName);
- ffStrbufInitA(&ds->wmProtocolName, 0);
- detectWM(ds);
- if(ds->wmProcessName.length == 0)
+ ffStrbufInitS(&ds->wmProcessName, "quartz");
+ ffStrbufInitS(&ds->wmPrettyName, "Quartz Compositor");
+ ffStrbufInit(&ds->wmProtocolName);
+
+ if(instance->config.allowSlowOperations)
{
- ffStrbufAppendS(&ds->wmProcessName, "quartz");
- ffStrbufAppendS(&ds->wmPrettyName, "Quartz Compositor");
+ FF_STRBUF_AUTO_DESTROY name;
+ detectWMPlugin(&name);
+ if(name.length)
+ ffStrbufAppendF(&ds->wmPrettyName, " (with %s)", name.chars);
}
ffStrbufInit(&ds->deProcessName);
ffStrbufInit(&ds->dePrettyName);
- ffStrbufInitA(&ds->deVersion, 0);
+ ffStrbufInit(&ds->deVersion);
ffStrbufAppendS(&ds->deProcessName, "aqua");
ffStrbufAppendS(&ds->dePrettyName, "Aqua");
diff --git a/src/detection/displayserver/displayserver_android.c b/src/detection/displayserver/displayserver_nosupport.c
similarity index 100%
rename from src/detection/displayserver/displayserver_android.c
rename to src/detection/displayserver/displayserver_nosupport.c
diff --git a/src/detection/displayserver/displayserver_windows.c b/src/detection/displayserver/displayserver_windows.c
new file mode 100644
index 000000000..f39955b8b
--- /dev/null
+++ b/src/detection/displayserver/displayserver_windows.c
@@ -0,0 +1,57 @@
+#include "displayserver.h"
+#include "detection/os/os.h"
+
+#include
+#include
+
+void ffConnectDisplayServerImpl(FFDisplayServerResult* ds, const FFinstance* instance)
+{
+ FF_UNUSED(instance);
+
+ BOOL enabled;
+ if(SUCCEEDED(DwmIsCompositionEnabled(&enabled)) && enabled == TRUE)
+ {
+ ffStrbufInitS(&ds->wmProcessName, "dwm.exe");
+ ffStrbufInitS(&ds->wmPrettyName, "Desktop Window Manager");
+ }
+ else
+ {
+ ffStrbufInitS(&ds->wmProcessName, "internal");
+ ffStrbufInitS(&ds->wmPrettyName, "internal");
+ }
+ ffStrbufInit(&ds->wmProtocolName);
+ ffStrbufInit(&ds->deProcessName);
+ ffStrbufInit(&ds->dePrettyName);
+ ffStrbufInit(&ds->deVersion);
+ ffListInit(&ds->resolutions, sizeof(FFResolutionResult));
+
+ DISPLAY_DEVICEW displayDevice = { .cb = sizeof(DISPLAY_DEVICEW) };
+ for(DWORD devNum = 0; EnumDisplayDevicesW(NULL, devNum, &displayDevice, 0) != 0; ++devNum)
+ {
+ if(!(displayDevice.StateFlags & DISPLAY_DEVICE_ACTIVE))
+ continue;
+ DEVMODEW devMode = { .dmSize = sizeof(DEVMODEW) };
+ if(EnumDisplaySettingsW(displayDevice.DeviceName, ENUM_CURRENT_SETTINGS, &devMode) == 0)
+ continue;
+
+ ffdsAppendResolution(ds, devMode.dmPelsWidth, devMode.dmPelsHeight, devMode.dmDisplayFrequency);
+ }
+
+ //https://github.com/hykilpikonna/hyfetch/blob/master/neofetch#L2067
+ const FFOSResult* os = ffDetectOS(instance);
+ if(
+ ffStrbufEqualS(&os->version, "11") ||
+ ffStrbufEqualS(&os->version, "10") ||
+ ffStrbufEqualS(&os->version, "2022") ||
+ ffStrbufEqualS(&os->version, "2019") ||
+ ffStrbufEqualS(&os->version, "2016")
+ ) ffStrbufSetS(&ds->dePrettyName, "Fluent");
+ else if(
+ ffStrbufEqualS(&os->version, "8") ||
+ ffStrbufEqualS(&os->version, "81.") ||
+ ffStrbufEqualS(&os->version, "2012 R2") ||
+ ffStrbufEqualS(&os->version, "2012")
+ ) ffStrbufSetS(&ds->dePrettyName, "Metro");
+ else
+ ffStrbufSetS(&ds->dePrettyName, "Aero");
+}
diff --git a/src/detection/displayserver/linux/wayland.c b/src/detection/displayserver/linux/wayland.c
index 39743282c..37891149c 100644
--- a/src/detection/displayserver/linux/wayland.c
+++ b/src/detection/displayserver/linux/wayland.c
@@ -6,7 +6,7 @@
#ifdef FF_HAVE_WAYLAND
#include "common/library.h"
#include "common/io.h"
-#include
+#include "common/thread.h"
#include
#include
@@ -56,12 +56,12 @@ static void waylandOutputModeListener(void* data, struct wl_output* output, uint
if(!(flags & WL_OUTPUT_MODE_CURRENT) || width <= 0 || height <= 0)
return;
- static pthread_mutex_t mutex = PTHREAD_MUTEX_INITIALIZER;
- pthread_mutex_lock(&mutex);
+ static FFThreadMutex mutex = FF_THREAD_MUTEX_INITIALIZER;
+ ffThreadMutexLock(&mutex);
FFResolutionResult* result = ffListAdd(wldata->results);
- pthread_mutex_unlock(&mutex);
+ ffThreadMutexUnlock(&mutex);
result->width = (uint32_t) width;
result->height = (uint32_t) height;
@@ -102,7 +102,7 @@ static void waylandGlobalAddListener(void* data, struct wl_registry* registry, u
bool detectWayland(const FFinstance* instance, FFDisplayServerResult* result)
{
- FF_LIBRARY_LOAD(wayland, &instance->config.libWayland, false, "libwayland-client.so", 1)
+ FF_LIBRARY_LOAD(wayland, &instance->config.libWayland, false, "libwayland-client" FF_LIBRARY_EXTENSION, 1)
FF_LIBRARY_LOAD_SYMBOL(wayland, wl_display_connect, false)
FF_LIBRARY_LOAD_SYMBOL(wayland, wl_display_get_fd, false)
diff --git a/src/detection/displayserver/linux/wmde.c b/src/detection/displayserver/linux/wmde.c
index da942bbd7..0cb7c7493 100644
--- a/src/detection/displayserver/linux/wmde.c
+++ b/src/detection/displayserver/linux/wmde.c
@@ -9,7 +9,6 @@
#include
#include
#include
-#include
static const char* parseEnv()
{
diff --git a/src/detection/displayserver/linux/xcb.c b/src/detection/displayserver/linux/xcb.c
index 22d5fa87c..692be7808 100644
--- a/src/detection/displayserver/linux/xcb.c
+++ b/src/detection/displayserver/linux/xcb.c
@@ -91,7 +91,7 @@ static void xcbDetectWMfromEWMH(XcbPropertyData* data, xcb_connection_t* connect
void ffdsConnectXcb(const FFinstance* instance, FFDisplayServerResult* result)
{
- FF_LIBRARY_LOAD(xcb, &instance->config.libXcb, , "libxcb.so", 2)
+ FF_LIBRARY_LOAD(xcb, &instance->config.libXcb, , "libxcb" FF_LIBRARY_EXTENSION, 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,)
@@ -326,7 +326,7 @@ static void xcbRandrHandleScreen(XcbRandrData* data, xcb_screen_t* screen)
void ffdsConnectXcbRandr(const FFinstance* instance, FFDisplayServerResult* result)
{
- FF_LIBRARY_LOAD(xcbRandr, &instance->config.libXcbRandr, , "libxcb-randr.so", 1)
+ FF_LIBRARY_LOAD(xcbRandr, &instance->config.libXcbRandr, , "libxcb-randr" FF_LIBRARY_EXTENSION, 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,)
diff --git a/src/detection/displayserver/linux/xlib.c b/src/detection/displayserver/linux/xlib.c
index 2cdc91d3c..108de5946 100644
--- a/src/detection/displayserver/linux/xlib.c
+++ b/src/detection/displayserver/linux/xlib.c
@@ -9,12 +9,14 @@ typedef struct X11PropertyData
{
FF_LIBRARY_SYMBOL(XInternAtom)
FF_LIBRARY_SYMBOL(XGetWindowProperty)
+ FF_LIBRARY_SYMBOL(XFree)
} X11PropertyData;
static bool x11InitPropertyData(void* libraryHandle, X11PropertyData* propertyData)
{
FF_LIBRARY_LOAD_SYMBOL_PTR(libraryHandle, propertyData, XInternAtom, false)
FF_LIBRARY_LOAD_SYMBOL_PTR(libraryHandle, propertyData, XGetWindowProperty, false)
+ FF_LIBRARY_LOAD_SYMBOL_PTR(libraryHandle, propertyData, XFree, false)
return true;
}
@@ -29,7 +31,8 @@ static unsigned char* x11GetProperty(X11PropertyData* data, Display* display, Wi
unsigned long unused;
unsigned char* result = NULL;
- data->ffXGetWindowProperty(display, window, requestAtom, 0, 64, False, AnyPropertyType, &actualType, (int*) &unused, &unused, &unused, &result);
+ if(data->ffXGetWindowProperty(display, window, requestAtom, 0, 64, False, AnyPropertyType, &actualType, (int*) &unused, &unused, &unused, &result) != Success)
+ return NULL;
return result;
}
@@ -43,19 +46,20 @@ static void x11DetectWMFromEWMH(X11PropertyData* data, Display* display, FFDispl
if(wmWindow == NULL)
return;
- const char* wmName = (const char*) x11GetProperty(data, display, *wmWindow, "_NET_WM_NAME");
+ char* wmName = (char*) x11GetProperty(data, display, *wmWindow, "_NET_WM_NAME");
if(wmName == NULL)
- wmName = (const char*) x11GetProperty(data, display, *wmWindow, "WM_NAME");
+ wmName = (char*) x11GetProperty(data, display, *wmWindow, "WM_NAME");
- if(!ffStrSet(wmName))
- return;
+ if(ffStrSet(wmName))
+ ffStrbufSetS(&result->wmProcessName, wmName);
- ffStrbufSetS(&result->wmProcessName, wmName);
+ data->ffXFree(wmName);
+ data->ffXFree(wmWindow);
}
void ffdsConnectXlib(const FFinstance* instance, FFDisplayServerResult* result)
{
- FF_LIBRARY_LOAD(x11, &instance->config.libX11, , "libX11.so", 7, "libX11-xcb.so", 2)
+ FF_LIBRARY_LOAD(x11, &instance->config.libX11, , "libX11" FF_LIBRARY_EXTENSION, 7, "libX11-xcb" FF_LIBRARY_EXTENSION, 2)
FF_LIBRARY_LOAD_SYMBOL(x11, XOpenDisplay,)
FF_LIBRARY_LOAD_SYMBOL(x11, XCloseDisplay,)
@@ -259,7 +263,7 @@ static void xrandrHandleScreen(XrandrData* data, Screen* screen)
void ffdsConnectXrandr(const FFinstance* instance, FFDisplayServerResult* result)
{
- FF_LIBRARY_LOAD(xrandr, &instance->config.libXrandr, , "libXrandr.so", 3)
+ FF_LIBRARY_LOAD(xrandr, &instance->config.libXrandr, , "libXrandr" FF_LIBRARY_EXTENSION, 3)
FF_LIBRARY_LOAD_SYMBOL(xrandr, XOpenDisplay,)
FF_LIBRARY_LOAD_SYMBOL(xrandr, XCloseDisplay,)
diff --git a/src/detection/font/font.h b/src/detection/font/font.h
index de78e5a92..822815d2a 100644
--- a/src/detection/font/font.h
+++ b/src/detection/font/font.h
@@ -12,9 +12,10 @@ typedef struct FFFontResult
FFstrbuf error;
/**
- * Linux / BSD: QT, GTK2, GTK3, GTK4
- * MacOS: System, User, Monospace, Application
- * Other: Unset, Unset, Unset, Unset
+ * Linux / BSD: QT, GTK2, GTK3, GTK4
+ * MacOS: System, User, System Mono, User Mono
+ * Windows: Caption, Menu, Message, Status
+ * Other: Unset, Unset, Unset, Unset
*/
FFstrbuf fonts[FF_DETECT_FONT_NUM_FONTS];
} FFFontResult;
diff --git a/src/detection/font/font_apple.m b/src/detection/font/font_apple.m
index 91689f6e9..773c7705b 100644
--- a/src/detection/font/font_apple.m
+++ b/src/detection/font/font_apple.m
@@ -1,23 +1,14 @@
#include "common/font.h"
#include "common/io.h"
-#include "util/apple/cf_helpers.h"
#include "font.h"
-#import
-
-static void detectFontForType(CTFontUIFontType uiType, FFstrbuf* font)
-{
- CTFontRef ctFont = CTFontCreateUIFontForLanguage(uiType, 12, NULL);
- ffCfStrGetString(CTFontCopyFamilyName(ctFont), font);
-}
+#import
void ffDetectFontImpl(const FFinstance* instance, FFFontResult* result)
{
FF_UNUSED(instance);
- ffSuppressIO(true);
- detectFontForType(kCTFontUIFontSystem, &result->fonts[0]);
- detectFontForType(kCTFontUIFontUser, &result->fonts[1]);
- detectFontForType(kCTFontUIFontUserFixedPitch, &result->fonts[2]);
- detectFontForType(kCTFontUIFontApplication, &result->fonts[3]);
- ffSuppressIO(false);
+ ffStrbufAppendS(&result->fonts[0], [NSFont systemFontOfSize:12].familyName.UTF8String);
+ ffStrbufAppendS(&result->fonts[1], [NSFont userFontOfSize:12].familyName.UTF8String);
+ ffStrbufAppendS(&result->fonts[2], [NSFont monospacedSystemFontOfSize:12 weight:400].familyName.UTF8String);
+ ffStrbufAppendS(&result->fonts[3], [NSFont userFixedPitchFontOfSize:12].familyName.UTF8String);
}
diff --git a/src/detection/font/font_android.c b/src/detection/font/font_nosupport.c
similarity index 66%
rename from src/detection/font/font_android.c
rename to src/detection/font/font_nosupport.c
index f2331a302..f88ad8395 100644
--- a/src/detection/font/font_android.c
+++ b/src/detection/font/font_nosupport.c
@@ -4,5 +4,5 @@
void ffDetectFontImpl(const FFinstance* instance, FFFontResult* result)
{
FF_UNUSED(instance);
- ffStrbufAppendS(&result->error, "Not implemented");
+ ffStrbufAppendS(&result->error, "Not supported on this platform");
}
diff --git a/src/detection/font/font_windows.c b/src/detection/font/font_windows.c
new file mode 100644
index 000000000..d858b8b42
--- /dev/null
+++ b/src/detection/font/font_windows.c
@@ -0,0 +1,22 @@
+#include "font.h"
+#include "util/windows/unicode.h"
+
+#include
+
+void ffDetectFontImpl(const FFinstance* instance, FFFontResult* result)
+{
+ FF_UNUSED(instance);
+
+ NONCLIENTMETRICSW info = { .cbSize = sizeof(info) };
+ if(!SystemParametersInfoW(SPI_GETNONCLIENTMETRICS, sizeof(info), &info, 0))
+ ffStrbufAppendS(&result->error, "SystemParametersInfoW(SPI_GETNONCLIENTMETRICS) failed");
+
+ LOGFONTW* fonts[4] = { &info.lfCaptionFont, &info.lfMenuFont, &info.lfMessageFont, &info.lfStatusFont };
+
+ for(uint32_t i = 0; i < sizeof(fonts) / sizeof(fonts[0]); ++i)
+ {
+ ffStrbufSetWS(&result->fonts[i], fonts[i]->lfFaceName);
+ if(fonts[i]->lfHeight < 0)
+ ffStrbufAppendF(&result->fonts[i], " (%dpt)", (int)-fonts[i]->lfHeight);
+ }
+}
diff --git a/src/detection/gpu/gpu.h b/src/detection/gpu/gpu.h
index 08b90d3be..16bd2ff53 100644
--- a/src/detection/gpu/gpu.h
+++ b/src/detection/gpu/gpu.h
@@ -8,6 +8,10 @@
#define FF_GPU_TEMP_UNSET (0/0.0)
#define FF_GPU_CORE_COUNT_UNSET -1
+#define FF_GPU_VENDOR_NAME_AMD "AMD"
+#define FF_GPU_VENDOR_NAME_INTEL "Intel"
+#define FF_GPU_VENDOR_NAME_NVIDIA "NVIDIA"
+
typedef struct FFGPUResult
{
FFstrbuf vendor;
diff --git a/src/detection/gpu/gpu_apple.c b/src/detection/gpu/gpu_apple.c
index bad6747d3..52292d83e 100644
--- a/src/detection/gpu/gpu_apple.c
+++ b/src/detection/gpu/gpu_apple.c
@@ -8,7 +8,7 @@
static double detectGpuTemp(const FFstrbuf* gpuName)
{
- FFlist temps;
+ FF_LIST_AUTO_DESTROY temps;
ffListInit(&temps, sizeof(FFTempValue));
if(ffStrbufStartsWithS(gpuName, "Apple M1"))
@@ -35,7 +35,6 @@ static double detectGpuTemp(const FFstrbuf* gpuName)
ffStrbufDestroy(&tempValue->deviceClass);
}
result /= temps.length;
- ffListDestroy(&temps);
return result;
}
diff --git a/src/detection/gpu/gpu_linux.c b/src/detection/gpu/gpu_linux.c
index 8266357ed..50580a898 100644
--- a/src/detection/gpu/gpu_linux.c
+++ b/src/detection/gpu/gpu_linux.c
@@ -1,10 +1,6 @@
#include "gpu.h"
#include "detection/vulkan.h"
-#define FF_GPU_VENDOR_NAME_AMD "AMD"
-#define FF_GPU_VENDOR_NAME_INTEL "Intel"
-#define FF_GPU_VENDOR_NAME_NVIDIA "NVIDIA"
-
#ifdef FF_HAVE_LIBPCI
#include "common/library.h"
#include "common/properties.h"
@@ -208,7 +204,7 @@ static const char* pciDetectGPUs(const FFinstance* instance, FFlist* gpus)
{
PCIData pci;
- FF_LIBRARY_LOAD(libpci, &instance->config.libPCI, "dlopen libpci.so failed", "libpci.so", 4);
+ FF_LIBRARY_LOAD(libpci, &instance->config.libPCI, "dlopen libpci.so failed", "libpci" FF_LIBRARY_EXTENSION, 4);
FF_LIBRARY_LOAD_SYMBOL_MESSAGE(libpci, pci_alloc);
FF_LIBRARY_LOAD_SYMBOL_MESSAGE(libpci, pci_init);
FF_LIBRARY_LOAD_SYMBOL_MESSAGE(libpci, pci_scan_bus);
diff --git a/src/detection/gpu/gpu_android.c b/src/detection/gpu/gpu_nosupport.c
similarity index 73%
rename from src/detection/gpu/gpu_android.c
rename to src/detection/gpu/gpu_nosupport.c
index 6e0afe389..ef2d54abd 100644
--- a/src/detection/gpu/gpu_android.c
+++ b/src/detection/gpu/gpu_nosupport.c
@@ -3,5 +3,5 @@
const char* ffDetectGPUImpl(FFlist* gpus, const FFinstance* instance)
{
FF_UNUSED(gpus, instance);
- return "Unimplemented";
+ return "Not supported on this platform";
}
diff --git a/src/detection/gpu/gpu_windows.cpp b/src/detection/gpu/gpu_windows.cpp
new file mode 100644
index 000000000..f5d0f4946
--- /dev/null
+++ b/src/detection/gpu/gpu_windows.cpp
@@ -0,0 +1,101 @@
+extern "C" {
+#include "gpu.h"
+#include "util/windows/unicode.h"
+}
+
+#ifdef FF_USE_WIN_GPU_DXGI
+
+#include
+#include
+
+static const char* detectWithDxgi(FFlist* gpus)
+{
+ IDXGIFactory1* pFactory;
+ if(FAILED(CreateDXGIFactory1(__uuidof(IDXGIFactory1), (void**)(&pFactory))))
+ return "CreateDXGIFactory1() failed";
+
+ for(unsigned iAdapter = 0;; ++iAdapter)
+ {
+ IDXGIAdapter1* adapter;
+ if(FAILED(pFactory->EnumAdapters1(iAdapter, &adapter)))
+ break;
+
+ DXGI_ADAPTER_DESC1 desc;
+ if(FAILED(adapter->GetDesc1(&desc)) || (desc.Flags & DXGI_ADAPTER_FLAG_SOFTWARE))
+ continue;
+
+ FFGPUResult* gpu = (FFGPUResult*)ffListAdd(gpus);
+
+ if(wmemchr((const wchar_t[]) {0x1002, 0x1022}, (wchar_t)desc.VendorId, 2))
+ ffStrbufInitS(&gpu->vendor, FF_GPU_VENDOR_NAME_AMD);
+ else if(wmemchr((const wchar_t[]) {0x03e7, 0x8086, 0x8087}, (wchar_t)desc.VendorId, 3))
+ ffStrbufInitS(&gpu->vendor, FF_GPU_VENDOR_NAME_INTEL);
+ else if(wmemchr((const wchar_t[]) {0x0955, 0x10de, 0x12d2}, (wchar_t)desc.VendorId, 3))
+ ffStrbufInitS(&gpu->vendor, FF_GPU_VENDOR_NAME_NVIDIA);
+ else
+ ffStrbufInit(&gpu->vendor);
+
+ ffStrbufInit(&gpu->name);
+ ffStrbufSetWS(&gpu->name, desc.Description);
+
+ ffStrbufInit(&gpu->driver);
+
+ adapter->Release();
+
+ gpu->temperature = FF_GPU_TEMP_UNSET;
+ gpu->coreCount = FF_GPU_CORE_COUNT_UNSET;
+ }
+
+ pFactory->Release();
+
+ return NULL;
+}
+
+#else
+
+#include "util/windows/wmi.hpp"
+
+static const char* detectWithWmi(FFlist* gpus)
+{
+ FFWmiQuery query(L"SELECT Name, AdapterCompatibility, DriverVersion FROM Win32_VideoController", nullptr);
+ if(!query)
+ return "Query WMI service failed";
+
+ while(FFWmiRecord record = query.next())
+ {
+ FFGPUResult* gpu = (FFGPUResult*)ffListAdd(gpus);
+
+ ffStrbufInit(&gpu->vendor);
+ record.getString(L"AdapterCompatibility", &gpu->vendor);
+ if(ffStrbufStartsWithS(&gpu->vendor, "Intel "))
+ {
+ //Intel returns "Intel Corporation", not sure about AMD
+ ffStrbufSetS(&gpu->vendor, "Intel");
+ }
+
+ ffStrbufInit(&gpu->name);
+ record.getString(L"Name", &gpu->name);
+
+ ffStrbufInit(&gpu->driver);
+ record.getString(L"DriverVersion", &gpu->driver);
+
+ gpu->temperature = FF_GPU_TEMP_UNSET;
+ gpu->coreCount = FF_GPU_CORE_COUNT_UNSET;
+ }
+
+ return nullptr;
+}
+
+#endif
+
+extern "C"
+const char* ffDetectGPUImpl(FFlist* gpus, const FFinstance* instance)
+{
+ FF_UNUSED(instance);
+
+ #ifdef FF_USE_WIN_GPU_DXGI
+ return detectWithDxgi(gpus);
+ #else
+ return detectWithWmi(gpus);
+ #endif
+}
diff --git a/src/detection/gtk.c b/src/detection/gtk.c
index 3855f7990..ad24c9c4e 100644
--- a/src/detection/gtk.c
+++ b/src/detection/gtk.c
@@ -1,11 +1,10 @@
#include "fastfetch.h"
#include "detection/gtk.h"
#include "common/properties.h"
+#include "common/thread.h"
#include "common/settings.h"
#include "detection/displayserver/displayserver.h"
-#include
-
static inline bool allPropertiesSet(FFGTKResult* result)
{
return
@@ -34,7 +33,7 @@ static inline void applyGTKSettings(FFGTKResult* result, const char* themeName,
static void detectGTKFromSettings(const FFinstance* instance, FFGTKResult* result)
{
- static pthread_mutex_t mutex = PTHREAD_MUTEX_INITIALIZER;
+ static FFThreadMutex mutex = FF_THREAD_MUTEX_INITIALIZER;
static const char* themeName = NULL;
static const char* iconsName = NULL;
@@ -44,11 +43,11 @@ static void detectGTKFromSettings(const FFinstance* instance, FFGTKResult* resul
static bool init = false;
- pthread_mutex_lock(&mutex);
+ ffThreadMutexLock(&mutex);
if(init)
{
- pthread_mutex_unlock(&mutex);
+ ffThreadMutexUnlock(&mutex);
applyGTKSettings(result, themeName, iconsName, fontName, cursorTheme, cursorSize);
return;
}
@@ -90,7 +89,7 @@ static void detectGTKFromSettings(const FFinstance* instance, FFGTKResult* resul
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);
+ ffThreadMutexUnlock(&mutex);
applyGTKSettings(result, themeName, iconsName, fontName, cursorTheme, cursorSize);
}
@@ -168,12 +167,12 @@ static void detectGTK(const FFinstance* instance, const char* version, FFGTKResu
}
#define FF_DETECT_GTK_IMPL(version) \
- static pthread_mutex_t mutex = PTHREAD_MUTEX_INITIALIZER; \
+ static FFThreadMutex mutex = FF_THREAD_MUTEX_INITIALIZER; \
static FFGTKResult result; \
static bool init = false; \
- pthread_mutex_lock(&mutex); \
+ ffThreadMutexLock(&mutex); \
if(init){ \
- pthread_mutex_unlock(&mutex);\
+ ffThreadMutexUnlock(&mutex);\
return &result; \
} \
init = true; \
@@ -183,7 +182,7 @@ static void detectGTK(const FFinstance* instance, const char* version, FFGTKResu
ffStrbufInit(&result.cursor); \
ffStrbufInit(&result.cursorSize); \
detectGTK(instance, #version, &result); \
- pthread_mutex_unlock(&mutex); \
+ ffThreadMutexUnlock(&mutex); \
return &result;
const FFGTKResult* ffDetectGTK2(const FFinstance* instance)
diff --git a/src/detection/host/host.h b/src/detection/host/host.h
index cdf2fbf53..c8b47b6b2 100644
--- a/src/detection/host/host.h
+++ b/src/detection/host/host.h
@@ -5,25 +5,14 @@
#include "fastfetch.h"
-#define FF_HOST_PRODUCT_NAME_WSL "Windows Subsystem for Linux"
-
typedef struct FFHostResult
{
FFstrbuf productFamily;
FFstrbuf productName;
FFstrbuf productVersion;
FFstrbuf productSku;
- FFstrbuf biosDate;
- FFstrbuf biosRelease;
- FFstrbuf biosVendor;
- FFstrbuf biosVersion;
- FFstrbuf boardName;
- FFstrbuf boardVendor;
- FFstrbuf boardVersion;
- FFstrbuf chassisType;
- FFstrbuf chassisVendor;
- FFstrbuf chassisVersion;
FFstrbuf sysVendor;
+ FFstrbuf error;
} FFHostResult;
const FFHostResult* ffDetectHost();
diff --git a/src/detection/host/host_android.c b/src/detection/host/host_android.c
index 1f2f10c11..edeb9cb47 100644
--- a/src/detection/host/host_android.c
+++ b/src/detection/host/host_android.c
@@ -4,6 +4,8 @@
void ffDetectHostImpl(FFHostResult* host)
{
+ ffStrbufInit(&host->error);
+
//Family
ffStrbufInit(&host->productFamily);
@@ -32,14 +34,4 @@ void ffDetectHostImpl(FFHostResult* host)
ffStrbufInitA(&host->productVersion, 0);
ffStrbufInitA(&host->productSku, 0);
- ffStrbufInitA(&host->biosDate, 0);
- ffStrbufInitA(&host->biosRelease, 0);
- ffStrbufInitA(&host->biosVendor, 0);
- ffStrbufInitA(&host->biosVersion, 0);
- ffStrbufInitA(&host->boardName, 0);
- ffStrbufInitA(&host->boardVendor, 0);
- ffStrbufInitA(&host->boardVersion, 0);
- ffStrbufInitA(&host->chassisType, 0);
- ffStrbufInitA(&host->chassisVendor, 0);
- ffStrbufInitA(&host->chassisVersion, 0);
}
diff --git a/src/detection/host/host_apple.c b/src/detection/host/host_apple.c
index f39edf0e0..d768d83e4 100644
--- a/src/detection/host/host_apple.c
+++ b/src/detection/host/host_apple.c
@@ -1,134 +1,159 @@
#include "host.h"
#include "common/sysctl.h"
+static inline bool strEqual(const char* a, const char* b)
+{
+ return strcmp(a, b) == 0;
+}
+
static const char* getProductName(const FFstrbuf* hwModel)
{
//https://github.com/hykilpikonna/hyfetch/blob/master/neofetch#L1386
- if(ffStrbufCompS(hwModel, "Mac14,7") == 0) return "MacBook Pro (13-inch, M2, 2022)";
- if(ffStrbufCompS(hwModel, "MacBookPro18,3") == 0 ||
- ffStrbufCompS(hwModel, "MacBookPro18,4") == 0) return "MacBook Pro (14-inch, 2021)";
- if(ffStrbufCompS(hwModel, "MacBookPro18,1") == 0 ||
- ffStrbufCompS(hwModel, "MacBookPro18,2") == 0) return "MacBook Pro (16-inch, 2021)";
- if(ffStrbufCompS(hwModel, "MacBookPro17,1") == 0) return "MacBook Pro (13-inch, M1, 2020)";
- if(ffStrbufCompS(hwModel, "MacBookPro16,4") == 0) return "MacBook Pro (16-inch, 2019)";
- if(ffStrbufCompS(hwModel, "MacBookPro16,3") == 0) return "MacBook Pro (13-inch, 2020, Two Thunderbolt 3 ports)";
- if(ffStrbufCompS(hwModel, "MacBookPro16,2") == 0) return "MacBook Pro (13-inch, 2020, Four Thunderbolt 3 ports)";
- if(ffStrbufCompS(hwModel, "MacBookPro16,1") == 0) return "MacBook Pro (16-inch, 2019)";
- if(ffStrbufCompS(hwModel, "MacBookPro15,4") == 0) return "MacBook Pro (13-inch, 2019, Two Thunderbolt 3 ports)";
- if(ffStrbufCompS(hwModel, "MacBookPro15,3") == 0) return "MacBook Pro (15-inch, 2019)";
- if(ffStrbufCompS(hwModel, "MacBookPro15,2") == 0) return "MacBook Pro (13-inch, 2018/2019, Four Thunderbolt 3 ports)";
- if(ffStrbufCompS(hwModel, "MacBookPro15,1") == 0) return "MacBook Pro (15-inch, 2018/2019)";
- if(ffStrbufCompS(hwModel, "MacBookPro14,3") == 0) return "MacBook Pro (15-inch, 2017)";
- if(ffStrbufCompS(hwModel, "MacBookPro14,2") == 0) return "MacBook Pro (13-inch, 2017, Four Thunderbolt 3 ports)";
- if(ffStrbufCompS(hwModel, "MacBookPro14,1") == 0) return "MacBook Pro (13-inch, 2017, Two Thunderbolt 3 ports)";
- if(ffStrbufCompS(hwModel, "MacBookPro13,3") == 0) return "MacBook Pro (15-inch, 2016)";
- if(ffStrbufCompS(hwModel, "MacBookPro13,2") == 0) return "MacBook Pro (13-inch, 2016, Four Thunderbolt 3 ports)";
- if(ffStrbufCompS(hwModel, "MacBookPro13,1") == 0) return "MacBook Pro (13-inch, 2016, Two Thunderbolt 3 ports)";
- if(ffStrbufCompS(hwModel, "MacBookPro12,1") == 0) return "MacBook Pro (Retina, 13-inch, Early 2015)";
- if(ffStrbufCompS(hwModel, "MacBookPro11,4") == 0 ||
- ffStrbufCompS(hwModel, "MacBookPro11,5") == 0) return "MacBook Pro (Retina, 15-inch, Mid 2015)";
- if(ffStrbufCompS(hwModel, "MacBookPro11,2") == 0 ||
- ffStrbufCompS(hwModel, "MacBookPro11,3") == 0) return "MacBook Pro (Retina, 15-inch, Late 2013/Mid 2014)";
- if(ffStrbufCompS(hwModel, "MacBookPro11,1") == 0) return "MacBook Pro (Retina, 13-inch, Late 2013/Mid 2014)";
- if(ffStrbufCompS(hwModel, "MacBookPro10,2") == 0) return "MacBook Pro (Retina, 13-inch, Late 2012/Early 2013)";
- if(ffStrbufCompS(hwModel, "MacBookPro10,1") == 0) return "MacBook Pro (Retina, 15-inch, Mid 2012/Early 2013)";
- if(ffStrbufCompS(hwModel, "MacBookPro9,2") == 0) return "MacBook Pro (13-inch, Mid 2012)";
- if(ffStrbufCompS(hwModel, "MacBookPro9,1") == 0) return "MacBook Pro (15-inch, Mid 2012)";
- if(ffStrbufCompS(hwModel, "MacBookPro8,3") == 0) return "MacBook Pro (17-inch, 2011)";
- if(ffStrbufCompS(hwModel, "MacBookPro8,2") == 0) return "MacBook Pro (15-inch, 2011)";
- if(ffStrbufCompS(hwModel, "MacBookPro8,1") == 0) return "MacBook Pro (13-inch, 2011)";
- if(ffStrbufCompS(hwModel, "MacBookPro7,1") == 0) return "MacBook Pro (13-inch, Mid 2010)";
- if(ffStrbufCompS(hwModel, "MacBookPro6,2") == 0) return "MacBook Pro (15-inch, Mid 2010)";
- if(ffStrbufCompS(hwModel, "MacBookPro6,1") == 0) return "MacBook Pro (17-inch, Mid 2010)";
- if(ffStrbufCompS(hwModel, "MacBookPro5,5") == 0) return "MacBook Pro (13-inch, Mid 2009)";
- if(ffStrbufCompS(hwModel, "MacBookPro5,3") == 0) return "MacBook Pro (15-inch, Mid 2009)";
- if(ffStrbufCompS(hwModel, "MacBookPro5,2") == 0) return "MacBook Pro (17-inch, Mid/Early 2009)";
- if(ffStrbufCompS(hwModel, "MacBookPro5,1") == 0) return "MacBook Pro (15-inch, Late 2008)";
- if(ffStrbufCompS(hwModel, "MacBookPro4,1") == 0) return "MacBook Pro (17/15-inch, Early 2008)";
- if(ffStrbufCompS(hwModel, "Mac14,2") == 0) return "MacBook Air (M2, 2022)";
- if(ffStrbufCompS(hwModel, "MacBookAir10,1") == 0) return "MacBook Air (M1, 2020)";
- if(ffStrbufCompS(hwModel, "MacBookAir9,1") == 0) return "MacBook Air (Retina, 13-inch, 2020)";
- if(ffStrbufCompS(hwModel, "MacBookAir8,2") == 0) return "MacBook Air (Retina, 13-inch, 2019)";
- if(ffStrbufCompS(hwModel, "MacBookAir8,1") == 0) return "MacBook Air (Retina, 13-inch, 2018)";
- if(ffStrbufCompS(hwModel, "MacBookAir7,2") == 0) return "MacBook Air (13-inch, Early 2015/2017)";
- if(ffStrbufCompS(hwModel, "MacBookAir7,1") == 0) return "MacBook Air (11-inch, Early 2015)";
- if(ffStrbufCompS(hwModel, "MacBookAir6,2") == 0) return "MacBook Air (13-inch, Mid 2013/Early 2014)";
- if(ffStrbufCompS(hwModel, "MacBookAir6,1") == 0) return "MacBook Air (11-inch, Mid 2013/Early 2014)";
- if(ffStrbufCompS(hwModel, "MacBookAir5,2") == 0) return "MacBook Air (13-inch, Mid 2012)";
- if(ffStrbufCompS(hwModel, "MacBookAir5,1") == 0) return "MacBook Air (11-inch, Mid 2012)";
- if(ffStrbufCompS(hwModel, "MacBookAir4,2") == 0) return "MacBook Air (13-inch, Mid 2011)";
- if(ffStrbufCompS(hwModel, "MacBookAir4,1") == 0) return "MacBook Air (11-inch, Mid 2011)";
- if(ffStrbufCompS(hwModel, "MacBookAir3,2") == 0) return "MacBook Air (13-inch, Late 2010)";
- if(ffStrbufCompS(hwModel, "MacBookAir3,1") == 0) return "MacBook Air (11-inch, Late 2010)";
- if(ffStrbufCompS(hwModel, "MacBookAir2,1") == 0) return "MacBook Air (Mid 2009)";
- if(ffStrbufCompS(hwModel, "MacBook10,1") == 0) return "MacBook (Retina, 12-inch, 2017)";
- if(ffStrbufCompS(hwModel, "MacBook9,1") == 0) return "MacBook (Retina, 12-inch, Early 2016)";
- if(ffStrbufCompS(hwModel, "MacBook8,1") == 0) return "MacBook (Retina, 12-inch, Early 2015)";
- if(ffStrbufCompS(hwModel, "MacBook7,1") == 0) return "MacBook (13-inch, Mid 2010)";
- if(ffStrbufCompS(hwModel, "MacBook6,1") == 0) return "MacBook (13-inch, Late 2009)";
- if(ffStrbufCompS(hwModel, "MacBook5,2") == 0) return "MacBook (13-inch, Early/Mid 2009)";
- if(ffStrbufCompS(hwModel, "Mac13,1") == 0) return "Mac Studio (2022, Two USB-C front ports)";
- if(ffStrbufCompS(hwModel, "Mac13,2") == 0) return "Mac Studio (2022, Two Thunderbolt 4 front ports)";
- if(ffStrbufCompS(hwModel, "Macmini9,1") == 0) return "Mac mini (M1, 2020)";
- if(ffStrbufCompS(hwModel, "Macmini8,1") == 0) return "Mac mini (2018)";
- if(ffStrbufCompS(hwModel, "Macmini7,1") == 0) return "Mac mini (Mid 2014)";
- if(ffStrbufCompS(hwModel, "Macmini6,1") == 0 ||
- ffStrbufCompS(hwModel, "Macmini6,2") == 0) return "Mac mini (Late 2012)";
- if(ffStrbufCompS(hwModel, "Macmini5,1") == 0 ||
- ffStrbufCompS(hwModel, "Macmini5,2") == 0) return "Mac mini (Mid 2011)";
- if(ffStrbufCompS(hwModel, "Macmini4,1") == 0) return "Mac mini (Mid 2010)";
- if(ffStrbufCompS(hwModel, "Macmini3,1") == 0) return "Mac mini (Early/Late 2009)";
- if(ffStrbufCompS(hwModel, "MacPro7,1") == 0) return "Mac Pro (2019)";
- if(ffStrbufCompS(hwModel, "MacPro6,1") == 0) return "Mac Pro (Late 2013)";
- if(ffStrbufCompS(hwModel, "MacPro5,1") == 0) return "Mac Pro (Mid 2010 - Mid 2012)";
- if(ffStrbufCompS(hwModel, "MacPro4,1") == 0) return "Mac Pro (Early 2009)";
- if(ffStrbufCompS(hwModel, "iMac21,1") == 0 ||
- ffStrbufCompS(hwModel, "iMac21,2") == 0) return "iMac (24-inch, M1, 2021)";
- if(ffStrbufCompS(hwModel, "iMac20,1") == 0 ||
- ffStrbufCompS(hwModel, "iMac20,2") == 0) return "iMac (Retina 5K, 27-inch, 2020)";
- if(ffStrbufCompS(hwModel, "iMac19,1") == 0 ||
- ffStrbufCompS(hwModel, "iMac19,2") == 0) return "iMac (Retina 4K, 21.5-inch, 2019)";
- if(ffStrbufCompS(hwModel, "iMacPro1,1") == 0) return "iMac Pro (2017)";
- if(ffStrbufCompS(hwModel, "iMac18,3") == 0) return "iMac (Retina 5K, 27-inch, 2017)";
- if(ffStrbufCompS(hwModel, "iMac18,2") == 0) return "iMac (Retina 4K, 21.5-inch, 2017)";
- if(ffStrbufCompS(hwModel, "iMac18,1") == 0) return "iMac (21.5-inch, 2017)";
- if(ffStrbufCompS(hwModel, "iMac17,1") == 0) return "iMac (Retina 5K, 27-inch, Late 2015)";
- if(ffStrbufCompS(hwModel, "iMac16,2") == 0) return "iMac (Retina 4K, 21.5-inch, Late 2015)";
- if(ffStrbufCompS(hwModel, "iMac16,1") == 0) return "iMac (21.5-inch, Late 2015)";
- if(ffStrbufCompS(hwModel, "iMac15,1") == 0) return "iMac (Retina 5K, 27-inch, Late 2014 - Mid 2015)";
- if(ffStrbufCompS(hwModel, "iMac14,4") == 0) return "iMac (21.5-inch, Mid 2014)";
- if(ffStrbufCompS(hwModel, "iMac14,2") == 0) return "iMac (27-inch, Late 2013)";
- if(ffStrbufCompS(hwModel, "iMac14,1") == 0) return "iMac (21.5-inch, Late 2013)";
- if(ffStrbufCompS(hwModel, "iMac13,2") == 0) return "iMac (27-inch, Late 2012)";
- if(ffStrbufCompS(hwModel, "iMac13,1") == 0) return "iMac (21.5-inch, Late 2012)";
- if(ffStrbufCompS(hwModel, "iMac12,2") == 0) return "iMac (27-inch, Mid 2011)";
- if(ffStrbufCompS(hwModel, "iMac12,1") == 0) return "iMac (21.5-inch, Mid 2011)";
- if(ffStrbufCompS(hwModel, "iMac11,3") == 0) return "iMac (27-inch, Mid 2010)";
- if(ffStrbufCompS(hwModel, "iMac11,2") == 0) return "iMac (21.5-inch, Mid 2010)";
- if(ffStrbufCompS(hwModel, "iMac10,1") == 0) return "iMac (27/21.5-inch, Late 2009)";
- if(ffStrbufCompS(hwModel, "iMac9,1") == 0) return "iMac (24/20-inch, Early 2009)";
+ if(ffStrbufStartsWithS(hwModel, "MacBookPro"))
+ {
+ const char* version = hwModel->chars + strlen("MacBookPro");
+ if(strEqual(version, "18,3") ||
+ strEqual(version, "18,4")) return "MacBook Pro (14-inch, 2021)";
+ if(strEqual(version, "18,1") ||
+ strEqual(version, "18,2")) return "MacBook Pro (16-inch, 2021)";
+ if(strEqual(version, "17,1")) return "MacBook Pro (13-inch, M1, 2020)";
+ if(strEqual(version, "16,4")) return "MacBook Pro (16-inch, 2019)";
+ if(strEqual(version, "16,3")) return "MacBook Pro (13-inch, 2020, Two Thunderbolt 3 ports)";
+ if(strEqual(version, "16,2")) return "MacBook Pro (13-inch, 2020, Four Thunderbolt 3 ports)";
+ if(strEqual(version, "16,1")) return "MacBook Pro (16-inch, 2019)";
+ if(strEqual(version, "15,4")) return "MacBook Pro (13-inch, 2019, Two Thunderbolt 3 ports)";
+ if(strEqual(version, "15,3")) return "MacBook Pro (15-inch, 2019)";
+ if(strEqual(version, "15,2")) return "MacBook Pro (13-inch, 2018/2019, Four Thunderbolt 3 ports)";
+ if(strEqual(version, "15,1")) return "MacBook Pro (15-inch, 2018/2019)";
+ if(strEqual(version, "14,3")) return "MacBook Pro (15-inch, 2017)";
+ if(strEqual(version, "14,2")) return "MacBook Pro (13-inch, 2017, Four Thunderbolt 3 ports)";
+ if(strEqual(version, "14,1")) return "MacBook Pro (13-inch, 2017, Two Thunderbolt 3 ports)";
+ if(strEqual(version, "13,3")) return "MacBook Pro (15-inch, 2016)";
+ if(strEqual(version, "13,2")) return "MacBook Pro (13-inch, 2016, Four Thunderbolt 3 ports)";
+ if(strEqual(version, "13,1")) return "MacBook Pro (13-inch, 2016, Two Thunderbolt 3 ports)";
+ if(strEqual(version, "12,1")) return "MacBook Pro (Retina, 13-inch, Early 2015)";
+ if(strEqual(version, "11,4") ||
+ strEqual(version, "11,5")) return "MacBook Pro (Retina, 15-inch, Mid 2015)";
+ if(strEqual(version, "11,2") ||
+ strEqual(version, "11,3")) return "MacBook Pro (Retina, 15-inch, Late 2013/Mid 2014)";
+ if(strEqual(version, "11,1")) return "MacBook Pro (Retina, 13-inch, Late 2013/Mid 2014)";
+ if(strEqual(version, "10,2")) return "MacBook Pro (Retina, 13-inch, Late 2012/Early 2013)";
+ if(strEqual(version, "10,1")) return "MacBook Pro (Retina, 15-inch, Mid 2012/Early 2013)";
+ if(strEqual(version, "9,2")) return "MacBook Pro (13-inch, Mid 2012)";
+ if(strEqual(version, "9,1")) return "MacBook Pro (15-inch, Mid 2012)";
+ if(strEqual(version, "8,3")) return "MacBook Pro (17-inch, 2011)";
+ if(strEqual(version, "8,2")) return "MacBook Pro (15-inch, 2011)";
+ if(strEqual(version, "8,1")) return "MacBook Pro (13-inch, 2011)";
+ if(strEqual(version, "7,1")) return "MacBook Pro (13-inch, Mid 2010)";
+ if(strEqual(version, "6,2")) return "MacBook Pro (15-inch, Mid 2010)";
+ if(strEqual(version, "6,1")) return "MacBook Pro (17-inch, Mid 2010)";
+ if(strEqual(version, "5,5")) return "MacBook Pro (13-inch, Mid 2009)";
+ if(strEqual(version, "5,3")) return "MacBook Pro (15-inch, Mid 2009)";
+ if(strEqual(version, "5,2")) return "MacBook Pro (17-inch, Mid/Early 2009)";
+ if(strEqual(version, "5,1")) return "MacBook Pro (15-inch, Late 2008)";
+ if(strEqual(version, "4,1")) return "MacBook Pro (17/15-inch, Early 2008)";
+ }
+ else if(ffStrbufStartsWithS(hwModel, "MacBookAir"))
+ {
+ const char* version = hwModel->chars + strlen("MacBookAir");
+ if(strEqual(version, "10,1")) return "MacBook Air (M1, 2020)";
+ if(strEqual(version, "9,1")) return "MacBook Air (Retina, 13-inch, 2020)";
+ if(strEqual(version, "8,2")) return "MacBook Air (Retina, 13-inch, 2019)";
+ if(strEqual(version, "8,1")) return "MacBook Air (Retina, 13-inch, 2018)";
+ if(strEqual(version, "7,2")) return "MacBook Air (13-inch, Early 2015/2017)";
+ if(strEqual(version, "7,1")) return "MacBook Air (11-inch, Early 2015)";
+ if(strEqual(version, "6,2")) return "MacBook Air (13-inch, Mid 2013/Early 2014)";
+ if(strEqual(version, "6,1")) return "MacBook Air (11-inch, Mid 2013/Early 2014)";
+ if(strEqual(version, "5,2")) return "MacBook Air (13-inch, Mid 2012)";
+ if(strEqual(version, "5,1")) return "MacBook Air (11-inch, Mid 2012)";
+ if(strEqual(version, "4,2")) return "MacBook Air (13-inch, Mid 2011)";
+ if(strEqual(version, "4,1")) return "MacBook Air (11-inch, Mid 2011)";
+ if(strEqual(version, "3,2")) return "MacBook Air (13-inch, Late 2010)";
+ if(strEqual(version, "3,1")) return "MacBook Air (11-inch, Late 2010)";
+ if(strEqual(version, "2,1")) return "MacBook Air (Mid 2009)";
+ }
+ else if(ffStrbufStartsWithS(hwModel, "Macmini"))
+ {
+ const char* version = hwModel->chars + strlen("Macmini");
+ if(strEqual(version, "9,1")) return "Mac mini (M1, 2020)";
+ if(strEqual(version, "8,1")) return "Mac mini (2018)";
+ if(strEqual(version, "7,1")) return "Mac mini (Mid 2014)";
+ if(strEqual(version, "6,1") ||
+ strEqual(version, "6,2")) return "Mac mini (Late 2012)";
+ if(strEqual(version, "5,1") ||
+ strEqual(version, "5,2")) return "Mac mini (Mid 2011)";
+ if(strEqual(version, "4,1")) return "Mac mini (Mid 2010)";
+ if(strEqual(version, "3,1")) return "Mac mini (Early/Late 2009)";
+ }
+ else if(ffStrbufStartsWithS(hwModel, "MacBook"))
+ {
+ const char* version = hwModel->chars + strlen("MacBook");
+ if(strEqual(version, "10,1")) return "MacBook (Retina, 12-inch, 2017)";
+ if(strEqual(version, "9,1")) return "MacBook (Retina, 12-inch, Early 2016)";
+ if(strEqual(version, "8,1")) return "MacBook (Retina, 12-inch, Early 2015)";
+ if(strEqual(version, "7,1")) return "MacBook (13-inch, Mid 2010)";
+ if(strEqual(version, "6,1")) return "MacBook (13-inch, Late 2009)";
+ if(strEqual(version, "5,2")) return "MacBook (13-inch, Early/Mid 2009)";
+ }
+ else if(ffStrbufStartsWithS(hwModel, "MacPro"))
+ {
+ const char* version = hwModel->chars + strlen("MacPro");
+ if(strEqual(version, "7,1")) return "Mac Pro (2019)";
+ if(strEqual(version, "6,1")) return "Mac Pro (Late 2013)";
+ if(strEqual(version, "5,1")) return "Mac Pro (Mid 2010 - Mid 2012)";
+ if(strEqual(version, "4,1")) return "Mac Pro (Early 2009)";
+ }
+ else if(ffStrbufStartsWithS(hwModel, "Mac"))
+ {
+ const char* version = hwModel->chars + strlen("Mac");
+ if(strEqual(version, "14,7")) return "MacBook Pro (13-inch, M2, 2022)";
+ if(strEqual(version, "14,2")) return "MacBook Air (M2, 2022)";
+ if(strEqual(version, "13,1")) return "Mac Studio (2022, Two USB-C front ports)";
+ if(strEqual(version, "13,2")) return "Mac Studio (2022, Two Thunderbolt 4 front ports)";
+ }
+ else if(ffStrbufStartsWithS(hwModel, "iMac"))
+ {
+ const char* version = hwModel->chars + strlen("iMac");
+ if(strEqual(version, "21,1") ||
+ strEqual(version, "21,2")) return "iMac (24-inch, M1, 2021)";
+ if(strEqual(version, "20,1") ||
+ strEqual(version, "20,2")) return "iMac (Retina 5K, 27-inch, 2020)";
+ if(strEqual(version, "19,1") ||
+ strEqual(version, "19,2")) return "iMac (Retina 4K, 21.5-inch, 2019)";
+ if(strEqual(version, "Pro1,1")) return "iMac Pro (2017)";
+ if(strEqual(version, "18,3")) return "iMac (Retina 5K, 27-inch, 2017)";
+ if(strEqual(version, "18,2")) return "iMac (Retina 4K, 21.5-inch, 2017)";
+ if(strEqual(version, "18,1")) return "iMac (21.5-inch, 2017)";
+ if(strEqual(version, "17,1")) return "iMac (Retina 5K, 27-inch, Late 2015)";
+ if(strEqual(version, "16,2")) return "iMac (Retina 4K, 21.5-inch, Late 2015)";
+ if(strEqual(version, "16,1")) return "iMac (21.5-inch, Late 2015)";
+ if(strEqual(version, "15,1")) return "iMac (Retina 5K, 27-inch, Late 2014 - Mid 2015)";
+ if(strEqual(version, "14,4")) return "iMac (21.5-inch, Mid 2014)";
+ if(strEqual(version, "14,2")) return "iMac (27-inch, Late 2013)";
+ if(strEqual(version, "14,1")) return "iMac (21.5-inch, Late 2013)";
+ if(strEqual(version, "13,2")) return "iMac (27-inch, Late 2012)";
+ if(strEqual(version, "13,1")) return "iMac (21.5-inch, Late 2012)";
+ if(strEqual(version, "12,2")) return "iMac (27-inch, Mid 2011)";
+ if(strEqual(version, "12,1")) return "iMac (21.5-inch, Mid 2011)";
+ if(strEqual(version, "11,3")) return "iMac (27-inch, Mid 2010)";
+ if(strEqual(version, "11,2")) return "iMac (21.5-inch, Mid 2010)";
+ if(strEqual(version, "10,1")) return "iMac (27/21.5-inch, Late 2009)";
+ if(strEqual(version, "9,1")) return "iMac (24/20-inch, Early 2009)";
+ }
return hwModel->chars;
}
void ffDetectHostImpl(FFHostResult* host)
{
+ ffStrbufInit(&host->error);
+
ffStrbufInit(&host->productName);
ffStrbufInit(&host->productFamily);
+ ffStrbufInit(&host->productVersion);
+ ffStrbufInit(&host->productSku);
+ ffStrbufInitS(&host->sysVendor, "Apple");
- ffStrbufInitA(&host->sysVendor, 0);
- ffStrbufInitA(&host->productVersion, 0);
- ffStrbufInitA(&host->productSku, 0);
- ffStrbufInitA(&host->biosDate, 0);
- ffStrbufInitA(&host->biosRelease, 0);
- ffStrbufInitA(&host->biosVendor, 0);
- ffStrbufInitA(&host->biosVersion, 0);
- ffStrbufInitA(&host->boardName, 0);
- ffStrbufInitA(&host->boardVendor, 0);
- ffStrbufInitA(&host->boardVersion, 0);
- ffStrbufInitA(&host->chassisType, 0);
- ffStrbufInitA(&host->chassisVendor, 0);
- ffStrbufInitA(&host->chassisVersion, 0);
-
- ffSysctlGetString("hw.model", &host->productFamily);
- ffStrbufAppendS(&host->productName, getProductName(&host->productFamily));
+ ffStrbufAppendS(&host->error, ffSysctlGetString("hw.model", &host->productFamily));
+ if(host->error.length == 0)
+ ffStrbufAppendS(&host->productName, getProductName(&host->productFamily));
}
diff --git a/src/detection/host/host_bsd.c b/src/detection/host/host_bsd.c
new file mode 100644
index 000000000..8be64b6c3
--- /dev/null
+++ b/src/detection/host/host_bsd.c
@@ -0,0 +1,15 @@
+#include "host.h"
+#include "common/sysctl.h"
+
+void ffDetectHostImpl(FFHostResult* host)
+{
+ ffStrbufInit(&host->error);
+
+ ffStrbufInit(&host->productName);
+ ffStrbufInit(&host->productFamily);
+ ffStrbufInit(&host->productVersion);
+ ffStrbufInit(&host->productSku);
+ ffStrbufInit(&host->sysVendor);
+
+ ffStrbufAppendS(&host->error, ffSysctlGetString("hw.fdt.model", &host->productName));
+}
diff --git a/src/detection/host/host_linux.c b/src/detection/host/host_linux.c
index 80e048ae7..e4e7dfef1 100644
--- a/src/detection/host/host_linux.c
+++ b/src/detection/host/host_linux.c
@@ -1,5 +1,6 @@
#include "host.h"
#include "common/io.h"
+#include "common/processing.h"
#include
@@ -60,6 +61,8 @@ static void getHostProductName(FFstrbuf* name)
void ffDetectHostImpl(FFHostResult* host)
{
+ ffStrbufInit(&host->error);
+
ffStrbufInit(&host->productFamily);
getHostValue("/sys/devices/virtual/dmi/id/product_family", "/sys/class/dmi/id/product_family", &host->productFamily);
@@ -72,36 +75,6 @@ void ffDetectHostImpl(FFHostResult* host)
ffStrbufInit(&host->productSku);
getHostValue("/sys/devices/virtual/dmi/id/product_sku", "/sys/class/dmi/id/product_sku", &host->productSku);
- ffStrbufInit(&host->biosDate);
- getHostValue("/sys/devices/virtual/dmi/id/bios_date", "/sys/class/dmi/id/bios_date", &host->biosDate);
-
- ffStrbufInit(&host->biosRelease);
- getHostValue("/sys/devices/virtual/dmi/id/bios_release", "/sys/class/dmi/id/bios_release", &host->biosRelease);
-
- ffStrbufInit(&host->biosVendor);
- getHostValue("/sys/devices/virtual/dmi/id/bios_vendor", "/sys/class/dmi/id/bios_vendor", &host->biosVendor);
-
- ffStrbufInit(&host->biosVersion);
- getHostValue("/sys/devices/virtual/dmi/id/bios_version", "/sys/class/dmi/id/bios_version", &host->biosVersion);
-
- ffStrbufInit(&host->boardName);
- getHostValue("/sys/devices/virtual/dmi/id/board_name", "/sys/class/dmi/id/board_name", &host->boardName);
-
- ffStrbufInit(&host->boardVendor);
- getHostValue("/sys/devices/virtual/dmi/id/board_vendor", "/sys/class/dmi/id/board_vendor", &host->boardVendor);
-
- ffStrbufInit(&host->boardVersion);
- getHostValue("/sys/devices/virtual/dmi/id/board_version", "/sys/class/dmi/id/board_version", &host->boardVersion);
-
- ffStrbufInit(&host->chassisType);
- getHostValue("/sys/devices/virtual/dmi/id/chassis_type", "/sys/class/dmi/id/chassis_type", &host->chassisType);
-
- ffStrbufInit(&host->chassisVendor);
- getHostValue("/sys/devices/virtual/dmi/id/chassis_vendor", "/sys/class/dmi/id/chassis_vendor", &host->chassisVendor);
-
- ffStrbufInit(&host->chassisVersion);
- getHostValue("/sys/devices/virtual/dmi/id/chassis_version", "/sys/class/dmi/id/chassis_version", &host->chassisVersion);
-
ffStrbufInit(&host->sysVendor);
getHostValue("/sys/devices/virtual/dmi/id/sys_vendor", "/sys/class/dmi/id/sys_vendor", &host->sysVendor);
@@ -109,10 +82,31 @@ void ffDetectHostImpl(FFHostResult* host)
if(ffStrbufStartsWithS(&host->productName, "Standard PC"))
ffStrbufPrependS(&host->productName, "KVM/QEMU ");
- //On WSL, the real host can't be detected. Instead use WSL as host.
- if(host->productFamily.length == 0 && host->productName.length == 0 && (
- getenv("WSLENV") != NULL ||
- getenv("WSL_DISTRO") != NULL ||
- getenv("WSL_INTEROP") != NULL
- )) ffStrbufAppendS(&host->productName, FF_HOST_PRODUCT_NAME_WSL);
+ if(host->productFamily.length == 0 && host->productName.length == 0)
+ {
+ //On WSL, the real host can't be detected. Instead use WSL as host.
+ if(getenv("WSL_DISTRO") != NULL || getenv("WSL_INTEROP") != NULL)
+ {
+ ffStrbufAppendS(&host->productName, "Windows Subsystem for Linux");
+
+ FFstrbuf wslVer; //Wide charactors
+ ffStrbufInit(&wslVer);
+ if(!ffProcessAppendStdOut(&wslVer, (char* const[]){
+ "wsl.exe",
+ "--version",
+ NULL
+ }) && wslVer.length > 0)
+ {
+ ffStrbufSubstrBeforeFirstC(&wslVer, '\r'); //CRLF
+ ffStrbufSubstrAfterLastC(&wslVer, ' ');
+ ffStrbufAppendS(&host->productName, " (");
+ for(uint32_t i = 0; i < wslVer.length; ++i) {
+ if(wslVer.chars[i]) //don't append \0
+ ffStrbufAppendC(&host->productName, wslVer.chars[i]);
+ }
+ ffStrbufAppendC(&host->productName, ')');
+ }
+ ffStrbufDestroy(&wslVer);
+ }
+ }
}
diff --git a/src/detection/host/host_windows.c b/src/detection/host/host_windows.c
new file mode 100644
index 000000000..75afd0563
--- /dev/null
+++ b/src/detection/host/host_windows.c
@@ -0,0 +1,24 @@
+#include "host.h"
+#include "util/windows/registry.h"
+
+void ffDetectHostImpl(FFHostResult* host)
+{
+ ffStrbufInit(&host->error);
+
+ ffStrbufInit(&host->productName);
+ ffStrbufInit(&host->productFamily);
+ ffStrbufInit(&host->productVersion);
+ ffStrbufInit(&host->productSku);
+ ffStrbufInit(&host->sysVendor);
+
+ FF_HKEY_AUTO_DESTROY hKey = NULL;
+
+ if(!ffRegOpenKeyForRead(HKEY_LOCAL_MACHINE, L"HARDWARE\\DESCRIPTION\\System\\BIOS", &hKey, &host->error))
+ return;
+
+ ffRegReadStrbuf(hKey, L"SystemProductName", &host->productName, NULL);
+ ffRegReadStrbuf(hKey, L"SystemFamily", &host->productFamily, NULL);
+ ffRegReadStrbuf(hKey, L"SystemVersion", &host->productVersion, NULL);
+ ffRegReadStrbuf(hKey, L"SystemSKU", &host->productSku, NULL);
+ ffRegReadStrbuf(hKey, L"SystemManufacturer", &host->sysVendor, NULL);
+}
diff --git a/src/detection/internal.h b/src/detection/internal.h
index f4dc67cbb..11b081f7e 100644
--- a/src/detection/internal.h
+++ b/src/detection/internal.h
@@ -3,21 +3,21 @@
#ifndef FF_INCLUDED_detection_internal
#define FF_INCLUDED_detection_internal
-#include "pthread.h"
+#include "common/thread.h"
#define FF_DETECTION_INTERNAL_GUARD(ResultType, ...) \
- static pthread_mutex_t mutex = PTHREAD_MUTEX_INITIALIZER; \
+ static FFThreadMutex mutex = FF_THREAD_MUTEX_INITIALIZER; \
static ResultType result; \
static bool init = false; \
- pthread_mutex_lock(&mutex); \
+ ffThreadMutexLock(&mutex); \
if(init) \
{ \
- pthread_mutex_unlock(&mutex); \
+ ffThreadMutexUnlock(&mutex); \
return &result; \
} \
init = true; \
__VA_ARGS__; \
- pthread_mutex_unlock(&mutex); \
+ ffThreadMutexUnlock(&mutex); \
return &result; \
#endif
diff --git a/src/detection/locale/locale.c b/src/detection/locale/locale.c
new file mode 100644
index 000000000..7b9e085cf
--- /dev/null
+++ b/src/detection/locale/locale.c
@@ -0,0 +1,61 @@
+#include "detection/locale/locale.h"
+
+#include "common/properties.h"
+#include "common/parsing.h"
+#include
+#include
+
+__attribute__((__unused__))
+static void getLocaleFromEnv(FFstrbuf* locale)
+{
+ ffStrbufAppendS(locale, getenv("LANG"));
+ if(locale->length > 0)
+ return;
+
+ ffStrbufAppendS(locale, getenv("LC_ALL"));
+ if(locale->length > 0)
+ return;
+
+ ffStrbufAppendS(locale, getenv("LC_MESSAGES"));
+}
+
+static void getLocaleFromStdFn(FFstrbuf* locale)
+{
+ ffStrbufAppendS(locale, setlocale(LC_ALL, NULL));
+
+ #ifdef LC_MESSAGES
+ if(locale->length > 0)
+ return;
+
+ ffStrbufAppendS(locale, setlocale(LC_MESSAGES, NULL));
+ #endif
+}
+
+void ffDetectLocale(FFstrbuf* result)
+{
+ #if !(defined(__APPLE__) || defined(_WIN32))
+
+ //Ubuntu (and deriviates) use a non standard locale file.
+ //Parse it first, because on distributions where it exists, it takes precedence.
+ //Otherwise use the standard etc/locale.conf file.
+ ffParsePropFile(FASTFETCH_TARGET_DIR_ETC"/default/locale", "LANG =", result);
+
+ if(result->length > 0)
+ return;
+
+ ffParsePropFile(FASTFETCH_TARGET_DIR_ETC"/locale.conf", "LANG =", result);
+ if(result->length > 0)
+ return;
+
+ #endif
+
+ #ifndef _WIN32
+
+ getLocaleFromEnv(result);
+ if(result->length > 0)
+ return;
+
+ #endif
+
+ getLocaleFromStdFn(result);
+}
diff --git a/src/detection/locale/locale.h b/src/detection/locale/locale.h
new file mode 100644
index 000000000..6ca84ab6f
--- /dev/null
+++ b/src/detection/locale/locale.h
@@ -0,0 +1,10 @@
+#pragma once
+
+#ifndef FF_INCLUDED_detection_locale_locale
+#define FF_INCLUDED_detection_locale_locale
+
+#include "fastfetch.h"
+
+void ffDetectLocale(FFstrbuf* result);
+
+#endif
diff --git a/src/detection/localip/localip.h b/src/detection/localip/localip.h
new file mode 100644
index 000000000..ef16da50f
--- /dev/null
+++ b/src/detection/localip/localip.h
@@ -0,0 +1,17 @@
+#pragma once
+
+#ifndef FF_INCLUDED_detection_localip_localip
+#define FF_INCLUDED_detection_localip_localip
+
+#include "fastfetch.h"
+
+typedef struct FFLocalIpResult
+{
+ FFstrbuf name;
+ FFstrbuf addr;
+ bool ipv6;
+} FFLocalIpResult;
+
+const char* ffDetectLocalIps(const FFinstance* instance, FFlist* results);
+
+#endif
diff --git a/src/detection/localip/localip_linux.c b/src/detection/localip/localip_linux.c
new file mode 100644
index 000000000..b38958b19
--- /dev/null
+++ b/src/detection/localip/localip_linux.c
@@ -0,0 +1,66 @@
+#include "localip.h"
+
+#include
+#include
+#include
+#include
+#include
+#include
+
+#ifdef __FreeBSD__
+ #include // FreeBSD needs this for AF_INET
+#endif
+
+static void addNewIp(FFlist* list, const char* name, const char* addr, bool ipv6)
+{
+ FFLocalIpResult* ip = (FFLocalIpResult*) ffListAdd(list);
+ ffStrbufInitS(&ip->name, name);
+ ffStrbufInitS(&ip->addr, addr);
+ ip->ipv6 = ipv6;
+}
+
+const char* ffDetectLocalIps(const FFinstance* instance, FFlist* results)
+{
+ struct ifaddrs* ifAddrStruct = NULL;
+ if(getifaddrs(&ifAddrStruct) < 0)
+ return "getifaddrs(&ifAddrStruct) failed";
+
+ for (struct ifaddrs* ifa = ifAddrStruct; ifa; ifa = ifa->ifa_next)
+ {
+ if (!ifa->ifa_addr || !(ifa->ifa_flags & IFF_RUNNING))
+ continue;
+
+ // loop back
+ if (strncmp(ifa->ifa_name, "lo", 2) == 0 && (ifa->ifa_name[2] == '\0' || isdigit(ifa->ifa_name[2])) && !instance->config.localIpShowLoop)
+ continue;
+
+ if (instance->config.localIpNamePrefix.length && strncmp(ifa->ifa_name, instance->config.localIpNamePrefix.chars, instance->config.localIpNamePrefix.length) != 0)
+ continue;
+
+ if (ifa->ifa_addr->sa_family == AF_INET)
+ {
+ // IPv4
+ if (!instance->config.localIpShowIpV4)
+ continue;
+
+ struct sockaddr_in* ipv4 = (struct sockaddr_in*) ifa->ifa_addr;
+ char addressBuffer[INET_ADDRSTRLEN];
+ inet_ntop(AF_INET, &ipv4->sin_addr, addressBuffer, INET_ADDRSTRLEN);
+ addNewIp(results, ifa->ifa_name, addressBuffer, false);
+ }
+ else if (ifa->ifa_addr->sa_family == AF_INET6)
+ {
+ // IPv6
+ if (!instance->config.localIpShowIpV6)
+ continue;
+
+ struct sockaddr_in6* ipv6 = (struct sockaddr_in6 *)ifa->ifa_addr;
+ char addressBuffer[INET6_ADDRSTRLEN];
+ inet_ntop(AF_INET6, &ipv6->sin6_addr, addressBuffer, INET6_ADDRSTRLEN);
+ addNewIp(results, ifa->ifa_name, addressBuffer, true);
+ }
+ }
+
+ if (ifAddrStruct) freeifaddrs(ifAddrStruct);
+ return NULL;
+}
diff --git a/src/detection/localip/localip_windows.c b/src/detection/localip/localip_windows.c
new file mode 100644
index 000000000..e23a32fe8
--- /dev/null
+++ b/src/detection/localip/localip_windows.c
@@ -0,0 +1,87 @@
+#include
+#include
+#include
+#include
+
+#include "localip.h"
+
+static void addNewIp(FFlist* list, const char* name, const char* addr, bool ipv6)
+{
+ FFLocalIpResult* ip = (FFLocalIpResult*) ffListAdd(list);
+ ffStrbufInitS(&ip->name, name);
+ ffStrbufInitS(&ip->addr, addr);
+ ip->ipv6 = ipv6;
+}
+
+const char* ffDetectLocalIps(const FFinstance* instance, FFlist* results)
+{
+ IP_ADAPTER_ADDRESSES* adapter_addresses = NULL;
+
+ // Start with a 16 KB buffer and resize if needed -
+ // multiple attempts in case interfaces change while
+ // we are in the middle of querying them.
+ DWORD adapter_addresses_buffer_size = 16 * 1024;
+ for (int attempts = 0; attempts != 3; ++attempts)
+ {
+ adapter_addresses = (IP_ADAPTER_ADDRESSES*)realloc(adapter_addresses, adapter_addresses_buffer_size);
+ assert(adapter_addresses);
+
+ DWORD error = GetAdaptersAddresses(
+ AF_UNSPEC,
+ GAA_FLAG_SKIP_ANYCAST |
+ GAA_FLAG_SKIP_MULTICAST |
+ GAA_FLAG_SKIP_DNS_SERVER |
+ GAA_FLAG_SKIP_FRIENDLY_NAME,
+ NULL,
+ adapter_addresses,
+ &adapter_addresses_buffer_size);
+
+ if (error == ERROR_SUCCESS)
+ break;
+ else if (ERROR_BUFFER_OVERFLOW == error)
+ continue;
+ else
+ return "GetAdaptersAddresses() failed";
+ }
+
+ // Iterate through all of the adapters
+ for (IP_ADAPTER_ADDRESSES* adapter = adapter_addresses; adapter; adapter = adapter->Next)
+ {
+ if (adapter->IfType == IF_TYPE_SOFTWARE_LOOPBACK && !instance->config.localIpShowLoop)
+ continue;
+
+ char name[128];
+ WideCharToMultiByte(CP_UTF8, 0, adapter->FriendlyName, -1, name, sizeof(name), NULL, NULL);
+ if (instance->config.localIpNamePrefix.length && strncmp(name, instance->config.localIpNamePrefix.chars, instance->config.localIpNamePrefix.length) != 0)
+ continue;
+
+ for (IP_ADAPTER_UNICAST_ADDRESS* ifa = adapter->FirstUnicastAddress; ifa; ifa = ifa->Next)
+ {
+ if (ifa->Address.lpSockaddr->sa_family == AF_INET)
+ {
+ // IPv4
+ if (!instance->config.localIpShowIpV4)
+ continue;
+
+ SOCKADDR_IN* ipv4 = (SOCKADDR_IN*) ifa->Address.lpSockaddr;
+ char addressBuffer[INET_ADDRSTRLEN];
+ inet_ntop(AF_INET, &ipv4->sin_addr, addressBuffer, INET_ADDRSTRLEN);
+ addNewIp(results, name, addressBuffer, false);
+ }
+ else if (ifa->Address.lpSockaddr->sa_family == AF_INET6)
+ {
+ // IPv6
+ if (!instance->config.localIpShowIpV6)
+ continue;
+
+ SOCKADDR_IN6* ipv6 = (SOCKADDR_IN6*) ifa->Address.lpSockaddr;
+ char addressBuffer[INET6_ADDRSTRLEN];
+ inet_ntop(AF_INET6, &ipv6->sin6_addr, addressBuffer, INET6_ADDRSTRLEN);
+ addNewIp(results, name, addressBuffer, false);
+ }
+ }
+ }
+
+ free(adapter_addresses);
+ return NULL;
+}
diff --git a/src/detection/media/media_linux.c b/src/detection/media/media_linux.c
index 77ecbc3a9..f8d410593 100644
--- a/src/detection/media/media_linux.c
+++ b/src/detection/media/media_linux.c
@@ -1,8 +1,8 @@
#include "fastfetch.h"
#include "detection/media/media.h"
+#include "common/thread.h"
#include
-#include
#define FF_DBUS_MPRIS_PREFIX "org.mpris.MediaPlayer2."
#define FF_DBUS_TIMEOUT_MILLISECONDS 35
@@ -295,7 +295,7 @@ static const char* getMedia(const FFinstance* instance, FFMediaResult* result)
{
DBusData data;
- FF_LIBRARY_LOAD(dbus, &instance->config.libDBus, "dlopen dbus failed", "libdbus-1.so", 4);
+ FF_LIBRARY_LOAD(dbus, &instance->config.libDBus, "dlopen dbus failed", "libdbus-1" FF_LIBRARY_EXTENSION, 4);
FF_LIBRARY_LOAD_SYMBOL_MESSAGE(dbus, dbus_bus_get)
FF_LIBRARY_LOAD_SYMBOL_VAR_MESSAGE(dbus, data, dbus_message_new_method_call)
FF_LIBRARY_LOAD_SYMBOL_VAR_MESSAGE(dbus, data, dbus_message_iter_init)
diff --git a/src/detection/media/media_android.c b/src/detection/media/media_nosupport.c
similarity index 63%
rename from src/detection/media/media_android.c
rename to src/detection/media/media_nosupport.c
index 0cf6d53d1..e3d038186 100644
--- a/src/detection/media/media_android.c
+++ b/src/detection/media/media_nosupport.c
@@ -3,5 +3,5 @@
void ffDetectMediaImpl(const FFinstance* instance, FFMediaResult* media)
{
FF_UNUSED(instance);
- ffStrbufAppendS(&media->error, "Media not supported on Android");
+ ffStrbufAppendS(&media->error, "Not supported on this platform");
}
diff --git a/src/detection/memory/memory.c b/src/detection/memory/memory.c
deleted file mode 100644
index f777554c3..000000000
--- a/src/detection/memory/memory.c
+++ /dev/null
@@ -1,27 +0,0 @@
-#include "memory.h"
-#include "detection/internal.h"
-
-void ffDetectMemoryImpl(FFMemoryResult* memory);
-
-static void calculatePercentage(FFMemoryStorage* storage)
-{
- if(storage->error.length != 0)
- return;
-
- if(storage->bytesTotal == 0)
- storage->percentage = 0;
- else
- storage->percentage = (uint8_t) ((storage->bytesUsed / (long double) storage->bytesTotal) * 100.0);
-}
-
-const FFMemoryResult* ffDetectMemory()
-{
- FF_DETECTION_INTERNAL_GUARD(FFMemoryResult,
- ffStrbufInitA(&result.ram.error, 0);
- ffStrbufInitA(&result.swap.error, 0);
-
- ffDetectMemoryImpl(&result);
- calculatePercentage(&result.ram);
- calculatePercentage(&result.swap);
- );
-}
diff --git a/src/detection/memory/memory.h b/src/detection/memory/memory.h
index a46d259c1..ee34c8038 100644
--- a/src/detection/memory/memory.h
+++ b/src/detection/memory/memory.h
@@ -3,22 +3,8 @@
#ifndef FF_INCLUDED_detection_memory_memory
#define FF_INCLUDED_detection_memory_memory
-#include "fastfetch.h"
+#include "detection/storage.h"
-typedef struct FFMemoryStorage
-{
- FFstrbuf error;
- uint64_t bytesUsed;
- uint64_t bytesTotal;
- uint8_t percentage;
-} FFMemoryStorage;
-
-typedef struct FFMemoryResult
-{
- FFMemoryStorage ram;
- FFMemoryStorage swap;
-} FFMemoryResult;
-
-const FFMemoryResult* ffDetectMemory();
+void ffDetectMemory(FFMemoryStorage* result);
#endif
diff --git a/src/detection/memory/memory_apple.c b/src/detection/memory/memory_apple.c
index 235b13443..f86f52cd4 100644
--- a/src/detection/memory/memory_apple.c
+++ b/src/detection/memory/memory_apple.c
@@ -4,7 +4,7 @@
#include
#include
-static void detectRam(FFMemoryStorage* ram)
+void ffDetectMemory(FFMemoryStorage* ram)
{
ram->bytesTotal = (uint64_t) ffSysctlGetInt64("hw.memsize", 0);
if(ram->bytesTotal == 0)
@@ -30,23 +30,3 @@ static void detectRam(FFMemoryStorage* ram)
ram->bytesUsed = ((uint64_t) vmstat.active_count + vmstat.wire_count) * pagesize;
}
-
-static void detectSwap(FFMemoryStorage* swap)
-{
- struct xsw_usage xsw;
- size_t size = sizeof(xsw);
- if(sysctlbyname("vm.swapusage", &xsw, &size, 0, 0) != 0)
- {
- ffStrbufAppendS(&swap->error, "Failed to read vm.swapusage");
- return;
- }
-
- swap->bytesTotal = xsw.xsu_total;
- swap->bytesUsed = xsw.xsu_used;
-}
-
-void ffDetectMemoryImpl(FFMemoryResult* memory)
-{
- detectRam(&memory->ram);
- detectSwap(&memory->swap);
-}
diff --git a/src/detection/memory/memory_bsd.c b/src/detection/memory/memory_bsd.c
index 55f34c83c..52d19a0c9 100644
--- a/src/detection/memory/memory_bsd.c
+++ b/src/detection/memory/memory_bsd.c
@@ -1,7 +1,7 @@
#include "memory.h"
#include "common/sysctl.h"
-static void detectRam(FFMemoryStorage* ram)
+void ffDetectMemory(FFMemoryStorage* ram)
{
uint32_t pageSize = (uint32_t) ffSysctlGetInt("hw.pagesize", 0);
if(pageSize == 0)
@@ -22,13 +22,3 @@ static void detectRam(FFMemoryStorage* ram)
- (uint64_t) ffSysctlGetInt64("vm.stats.vm.v_inactive_count", 0) * pageSize
;
}
-
-static void detectSwap(FFMemoryStorage* swap)
-{
- ffStrbufAppendS(&swap->error, "Not implemented");
-}
-void ffDetectMemoryImpl(FFMemoryResult* memory)
-{
- detectRam(&memory->ram);
- detectSwap(&memory->swap);
-}
diff --git a/src/detection/memory/memory_linux.c b/src/detection/memory/memory_linux.c
index b83819295..339dda4fe 100644
--- a/src/detection/memory/memory_linux.c
+++ b/src/detection/memory/memory_linux.c
@@ -3,13 +3,12 @@
#include
#include
-void ffDetectMemoryImpl(FFMemoryResult* memory)
+void ffDetectMemory(FFMemoryStorage* ram)
{
FILE* meminfo = fopen("/proc/meminfo", "r");
if(meminfo == NULL)
{
- ffStrbufAppendS(&memory->ram.error, "Failed to open /proc/meminfo");
- ffStrbufAppendS(&memory->swap.error, "Failed to open /proc/meminfo");
+ ffStrbufAppendS(&ram->error, "Failed to open /proc/meminfo");
return;
}
@@ -21,20 +20,16 @@ void ffDetectMemoryImpl(FFMemoryResult* memory)
memFree = 0,
buffers = 0,
cached = 0,
- sReclaimable = 0,
- swapTotal = 0,
- swapFree = 0;
+ sReclaimable = 0;
while (getline(&line, &len, meminfo) != EOF)
{
- sscanf(line, "MemTotal: %u", &memTotal);
- sscanf(line, "Shmem: %u", &shmem);
- sscanf(line, "MemFree: %u", &memFree);
- sscanf(line, "Buffers: %u", &buffers);
- sscanf(line, "Cached: %u", &cached);
- sscanf(line, "SReclaimable: %u", &sReclaimable);
- sscanf(line, "SwapTotal: %u", &swapTotal);
- sscanf(line, "SwapFree: %u", &swapFree);
+ if(!sscanf(line, "MemTotal: %u", &memTotal))
+ if(!sscanf(line, "Shmem: %u", &shmem))
+ if(!sscanf(line, "MemFree: %u", &memFree))
+ if(!sscanf(line, "Buffers: %u", &buffers))
+ if(!sscanf(line, "Cached: %u", &cached))
+ sscanf(line, "SReclaimable: %u", &sReclaimable);
}
if(line != NULL)
@@ -42,12 +37,9 @@ void ffDetectMemoryImpl(FFMemoryResult* memory)
fclose(meminfo);
- memory->ram.bytesTotal = memTotal * (uint64_t) 1024;
- if(memory->ram.bytesTotal == 0)
- ffStrbufAppendS(&memory->ram.error, "Failed to read MemTotal");
+ ram->bytesTotal = memTotal * (uint64_t) 1024;
+ if(ram->bytesTotal == 0)
+ ffStrbufAppendS(&ram->error, "Failed to read MemTotal");
else
- memory->ram.bytesUsed = (memTotal + shmem - memFree - buffers - cached - sReclaimable) * (uint64_t) 1024;
-
- memory->swap.bytesTotal = swapTotal * (uint64_t) 1024;
- memory->swap.bytesUsed = (swapTotal - swapFree) * (uint64_t) 1024;
+ ram->bytesUsed = (memTotal + shmem - memFree - buffers - cached - sReclaimable) * (uint64_t) 1024;
}
diff --git a/src/detection/memory/memory_windows.c b/src/detection/memory/memory_windows.c
new file mode 100644
index 000000000..ef3153843
--- /dev/null
+++ b/src/detection/memory/memory_windows.c
@@ -0,0 +1,11 @@
+#include "memory.h"
+
+void ffDetectMemory(FFMemoryStorage* ram)
+{
+ MEMORYSTATUSEX statex = {
+ .dwLength = sizeof(statex),
+ };
+ GlobalMemoryStatusEx(&statex);
+ ram->bytesTotal = statex.ullTotalPhys;
+ ram->bytesUsed = statex.ullTotalPhys - statex.ullAvailPhys;
+}
diff --git a/src/detection/opengl/opengl.h b/src/detection/opengl/opengl.h
new file mode 100644
index 000000000..206347184
--- /dev/null
+++ b/src/detection/opengl/opengl.h
@@ -0,0 +1,18 @@
+#pragma once
+
+#ifndef FF_INCLUDED_detection_opengl_opengl
+#define FF_INCLUDED_detection_opengl_opengl
+
+#include "fastfetch.h"
+
+typedef struct FFOpenGLResult
+{
+ FFstrbuf version;
+ FFstrbuf renderer;
+ FFstrbuf vendor;
+ FFstrbuf slv;
+} FFOpenGLResult;
+
+const char* ffDetectOpenGL(FFinstance* instance, FFOpenGLResult* result);
+
+#endif
diff --git a/src/detection/opengl/opengl_apple.c b/src/detection/opengl/opengl_apple.c
new file mode 100644
index 000000000..4ccbb5fe6
--- /dev/null
+++ b/src/detection/opengl/opengl_apple.c
@@ -0,0 +1,56 @@
+
+#include "fastfetch.h"
+#include "opengl.h"
+
+#define GL_SILENCE_DEPRECATION
+#include
+#include // This brings in CGL, not GL
+
+static const char* glHandleResult(FFOpenGLResult* result)
+{
+ ffStrbufAppendS(&result->version, (const char*) glGetString(GL_VERSION));
+ ffStrbufAppendS(&result->renderer, (const char*) glGetString(GL_RENDERER));
+ ffStrbufAppendS(&result->vendor, (const char*) glGetString(GL_VENDOR));
+ ffStrbufAppendS(&result->slv, (const char*) glGetString(GL_SHADING_LANGUAGE_VERSION));
+ return NULL;
+}
+
+static const char* cglHandleContext(FFOpenGLResult* result, CGLContextObj context)
+{
+ if(CGLSetCurrentContext(context) != kCGLNoError)
+ return "CGLSetCurrentContext() failed";
+
+ return glHandleResult(result);
+}
+
+static const char* cglHandlePixelFormat(FFOpenGLResult* result, CGLPixelFormatObj pixelFormat)
+{
+ CGLContextObj context;
+
+ if(CGLCreateContext(pixelFormat, NULL, &context) != kCGLNoError)
+ return "CGLCreateContext() failed";
+
+ const char* error = cglHandleContext(result, context);
+ CGLDestroyContext(context);
+ return error;
+}
+
+const char* ffDetectOpenGL(FFinstance* instance, FFOpenGLResult* result)
+{
+ FF_UNUSED(instance);
+
+ CGLPixelFormatObj pixelFormat;
+ CGLPixelFormatAttribute attrs[] = {
+ kCGLPFAOpenGLProfile, (CGLPixelFormatAttribute) kCGLOGLPVersion_3_2_Core,
+ kCGLPFAAccelerated,
+ 0
+ };
+
+ GLint num;
+ if (CGLChoosePixelFormat(attrs, &pixelFormat, &num) != kCGLNoError)
+ return "CGLChoosePixelFormat() failed";
+
+ const char* error = cglHandlePixelFormat(result, pixelFormat);
+ CGLDestroyPixelFormat(pixelFormat);
+ return error;
+}
diff --git a/src/detection/opengl/opengl_linux.c b/src/detection/opengl/opengl_linux.c
new file mode 100644
index 000000000..d95062ad5
--- /dev/null
+++ b/src/detection/opengl/opengl_linux.c
@@ -0,0 +1,373 @@
+#include "fastfetch.h"
+#include "opengl.h"
+
+#include
+
+#if defined(FF_HAVE_EGL) || defined(FF_HAVE_GLX) || defined(FF_HAVE_OSMESA)
+#define FF_HAVE_GL 1
+
+#include "common/library.h"
+
+#include
+
+#define FF_OPENGL_BUFFER_WIDTH 1
+#define FF_OPENGL_BUFFER_HEIGHT 1
+
+typedef struct GLData
+{
+ FF_LIBRARY_SYMBOL(glGetString)
+} GLData;
+
+static const char* glHandleResult(FFOpenGLResult* result, const GLData* data)
+{
+ ffStrbufAppendS(&result->version, (const char*) data->ffglGetString(GL_VERSION));
+ ffStrbufAppendS(&result->renderer, (const char*) data->ffglGetString(GL_RENDERER));
+ ffStrbufAppendS(&result->vendor, (const char*) data->ffglGetString(GL_VENDOR));
+ ffStrbufAppendS(&result->slv, (const char*) data->ffglGetString(GL_SHADING_LANGUAGE_VERSION));
+ return NULL;
+}
+
+#endif // FF_HAVE_GL
+
+#ifdef FF_HAVE_EGL
+#include
+
+typedef struct EGLData
+{
+ GLData glData;
+
+ FF_LIBRARY_SYMBOL(eglGetProcAddress)
+ FF_LIBRARY_SYMBOL(eglGetDisplay)
+ FF_LIBRARY_SYMBOL(eglInitialize)
+ FF_LIBRARY_SYMBOL(eglBindAPI)
+ FF_LIBRARY_SYMBOL(eglGetConfigs)
+ FF_LIBRARY_SYMBOL(eglCreatePbufferSurface)
+ FF_LIBRARY_SYMBOL(eglCreateContext)
+ FF_LIBRARY_SYMBOL(eglMakeCurrent)
+ FF_LIBRARY_SYMBOL(eglDestroyContext)
+ FF_LIBRARY_SYMBOL(eglDestroySurface)
+ FF_LIBRARY_SYMBOL(eglTerminate)
+
+ EGLDisplay display;
+ EGLConfig config;
+ EGLSurface surface;
+ EGLContext context;
+} EGLData;
+
+static const char* eglHandleContext(FFOpenGLResult* result, EGLData* data)
+{
+ if(data->ffeglMakeCurrent(data->display, data->surface, data->surface, data->context) != EGL_TRUE)
+ return "eglMakeCurrent returned EGL_FALSE";
+
+ return glHandleResult(result, &data->glData);
+}
+
+static const char* eglHandleSurface(FFOpenGLResult* result, EGLData* data)
+{
+ data->context = data->ffeglCreateContext(data->display, data->config, EGL_NO_CONTEXT, (EGLint[]){EGL_NONE});
+ if(data->context == EGL_NO_CONTEXT)
+ return "eglCreateContext returned EGL_NO_CONTEXT";
+
+ const char* error = eglHandleContext(result, data);
+ data->ffeglDestroyContext(data->display, data->context);
+ return error;
+}
+
+static const char* eglHandleDisplay(FFOpenGLResult* result, EGLData* data)
+{
+ if(data->ffeglBindAPI(EGL_OPENGL_API) != EGL_TRUE)
+ return "eglBindAPI returned EGL_FALSE";
+
+ EGLint eglConfigCount;
+ data->ffeglGetConfigs(data->display, &data->config, 1, &eglConfigCount);
+ if(eglConfigCount == 0)
+ return "eglGetConfigs returned 0 configs";
+
+ data->surface = data->ffeglCreatePbufferSurface(data->display, data->config, (EGLint[]){
+ EGL_WIDTH, FF_OPENGL_BUFFER_WIDTH,
+ EGL_HEIGHT, FF_OPENGL_BUFFER_HEIGHT,
+ EGL_NONE
+ });
+
+ if(data->surface == EGL_NO_SURFACE)
+ return "eglCreatePbufferSurface returned EGL_NO_SURFACE";
+
+ const char* error = eglHandleSurface(result, data);
+ data->ffeglDestroySurface(data->display, data->surface);
+ return error;
+}
+
+static const char* eglHandleData(FFOpenGLResult* result, EGLData* data)
+{
+ data->glData.ffglGetString = (__typeof__(data->glData.ffglGetString)) data->ffeglGetProcAddress("glGetString");
+ if(!data->glData.ffglGetString)
+ return "eglGetProcAddress(glGetString) returned NULL";
+
+ data->display = data->ffeglGetDisplay(EGL_DEFAULT_DISPLAY);
+ if(data->display == EGL_NO_DISPLAY)
+ return "eglGetDisplay returned EGL_NO_DISPLAY";
+
+ EGLint major, minor;
+ if(data->ffeglInitialize(data->display, &major, &minor) == EGL_FALSE)
+ return "eglInitialize returned EGL_FALSE";
+
+ const char* error = eglHandleDisplay(result, data);
+ data->ffeglTerminate(data->display);
+ return error;
+}
+
+static const char* eglPrint(FFinstance* instance, FFOpenGLResult* result)
+{
+ EGLData eglData;
+
+ FF_LIBRARY_LOAD(egl, &instance->config.libEGL, "dlopen egl failed", "libEGL" FF_LIBRARY_EXTENSION, 1);
+ FF_LIBRARY_LOAD_SYMBOL_VAR_MESSAGE(egl, eglData, eglGetProcAddress);
+ FF_LIBRARY_LOAD_SYMBOL_VAR_MESSAGE(egl, eglData, eglGetDisplay);
+ FF_LIBRARY_LOAD_SYMBOL_VAR_MESSAGE(egl, eglData, eglInitialize);
+ FF_LIBRARY_LOAD_SYMBOL_VAR_MESSAGE(egl, eglData, eglBindAPI);
+ FF_LIBRARY_LOAD_SYMBOL_VAR_MESSAGE(egl, eglData, eglGetConfigs);
+ FF_LIBRARY_LOAD_SYMBOL_VAR_MESSAGE(egl, eglData, eglCreatePbufferSurface);
+ FF_LIBRARY_LOAD_SYMBOL_VAR_MESSAGE(egl, eglData, eglCreateContext);
+ FF_LIBRARY_LOAD_SYMBOL_VAR_MESSAGE(egl, eglData, eglMakeCurrent);
+ FF_LIBRARY_LOAD_SYMBOL_VAR_MESSAGE(egl, eglData, eglDestroyContext);
+ FF_LIBRARY_LOAD_SYMBOL_VAR_MESSAGE(egl, eglData, eglDestroySurface);
+ FF_LIBRARY_LOAD_SYMBOL_VAR_MESSAGE(egl, eglData, eglTerminate);
+
+ const char* error = eglHandleData(result, &eglData);
+ dlclose(egl);
+ return error;
+}
+
+#endif //FF_HAVE_EGL
+
+#ifdef FF_HAVE_GLX
+#include
+
+typedef struct GLXData
+{
+ GLData glData;
+
+ FF_LIBRARY_SYMBOL(glXGetProcAddress)
+ FF_LIBRARY_SYMBOL(XOpenDisplay)
+ FF_LIBRARY_SYMBOL(glXChooseVisual)
+ FF_LIBRARY_SYMBOL(XCreatePixmap);
+ FF_LIBRARY_SYMBOL(glXCreateGLXPixmap)
+ FF_LIBRARY_SYMBOL(glXCreateContext)
+ FF_LIBRARY_SYMBOL(glXMakeCurrent)
+ FF_LIBRARY_SYMBOL(glXDestroyContext)
+ FF_LIBRARY_SYMBOL(glXDestroyGLXPixmap)
+ FF_LIBRARY_SYMBOL(XFreePixmap)
+ FF_LIBRARY_SYMBOL(XCloseDisplay)
+ FF_LIBRARY_SYMBOL(XFree)
+
+ Display* display;
+ XVisualInfo* visualInfo;
+ Pixmap pixmap;
+ GLXPixmap glxPixmap;
+ GLXContext context;
+} GLXData;
+
+static const char* glxHandleContext(FFOpenGLResult* result, GLXData* data)
+{
+ if(data->ffglXMakeCurrent(data->display, data->glxPixmap, data->context) != True)
+ return "glXMakeCurrent returned False";
+
+ return glHandleResult(result, &data->glData);
+}
+
+static const char* glxHandleGLXPixmap(FFOpenGLResult* result, GLXData* data)
+{
+ data->context = data->ffglXCreateContext(data->display, data->visualInfo, NULL, True);
+ if(data->context == NULL)
+ return "glXCreateContext returned NULL";
+
+ const char* error = glxHandleContext(result, data);
+ data->ffglXDestroyContext(data->display, data->context);
+ return error;
+}
+
+static const char* glxHandlePixmap(FFOpenGLResult* result, GLXData* data)
+{
+ data->glxPixmap = data->ffglXCreateGLXPixmap(data->display, data->visualInfo, data->pixmap);
+ if(data->glxPixmap == None)
+ return "glXCreateGLXPixmap returned None";
+
+ const char* error = glxHandleGLXPixmap(result, data);
+ data->ffglXDestroyGLXPixmap(data->display, data->glxPixmap);
+ return error;
+}
+
+static const char* glxHandleVisualInfo(FFOpenGLResult* result, GLXData* data)
+{
+ data->pixmap = data->ffXCreatePixmap(data->display, DefaultRootWindow(data->display), FF_OPENGL_BUFFER_WIDTH, FF_OPENGL_BUFFER_HEIGHT, (unsigned int) data->visualInfo->depth);
+ if(data->pixmap == None)
+ return "XCreatePixmap returned None";
+
+ const char* error = glxHandlePixmap(result, data);
+ data->ffXFreePixmap(data->display, data->pixmap);
+ return error;
+}
+
+static const char* glxHandleDisplay(FFOpenGLResult* result, GLXData* data)
+{
+ data->visualInfo = data->ffglXChooseVisual(data->display, DefaultScreen(data->display), (int[]){None});
+ if(data->visualInfo == NULL)
+ return "glXChooseVisual returned NULL";
+
+ const char* error = glxHandleVisualInfo(result, data);
+ data->ffXFree(data->visualInfo);
+ return error;
+}
+
+static const char* glxHandleData(FFOpenGLResult* result, GLXData* data)
+{
+ data->glData.ffglGetString = (__typeof__(data->glData.ffglGetString)) data->ffglXGetProcAddress((const GLubyte*) "glGetString");
+ if(data->glData.ffglGetString == NULL)
+ return "glXGetProcAddress(glGetString) returned NULL";
+
+ data->display = data->ffXOpenDisplay(NULL);
+ if(data->display == NULL)
+ return "XOpenDisplay returned NULL";
+
+ const char* error = glxHandleDisplay(result, data);
+ data->ffXCloseDisplay(data->display);
+ return error;
+}
+
+static const char* glxPrint(FFinstance* instance, FFOpenGLResult* result)
+{
+ GLXData data;
+
+ FF_LIBRARY_LOAD(glx, &instance->config.libGLX, "dlopen glx failed", "libGLX" FF_LIBRARY_EXTENSION, 1);
+ FF_LIBRARY_LOAD_SYMBOL_VAR_MESSAGE(glx, data, glXGetProcAddress);
+ FF_LIBRARY_LOAD_SYMBOL_VAR_MESSAGE(glx, data, XOpenDisplay);
+ FF_LIBRARY_LOAD_SYMBOL_VAR_MESSAGE(glx, data, glXChooseVisual);
+ FF_LIBRARY_LOAD_SYMBOL_VAR_MESSAGE(glx, data, XCreatePixmap);
+ FF_LIBRARY_LOAD_SYMBOL_VAR_MESSAGE(glx, data, glXCreateGLXPixmap);
+ FF_LIBRARY_LOAD_SYMBOL_VAR_MESSAGE(glx, data, glXCreateContext);
+ FF_LIBRARY_LOAD_SYMBOL_VAR_MESSAGE(glx, data, glXMakeCurrent);
+ FF_LIBRARY_LOAD_SYMBOL_VAR_MESSAGE(glx, data, glXDestroyContext);
+ FF_LIBRARY_LOAD_SYMBOL_VAR_MESSAGE(glx, data, glXDestroyGLXPixmap);
+ FF_LIBRARY_LOAD_SYMBOL_VAR_MESSAGE(glx, data, XFreePixmap);
+ FF_LIBRARY_LOAD_SYMBOL_VAR_MESSAGE(glx, data, XCloseDisplay);
+ FF_LIBRARY_LOAD_SYMBOL_VAR_MESSAGE(glx, data, XFree);
+
+ const char* error = glxHandleData(result, &data);
+ dlclose(glx);
+ return error;
+}
+
+#endif //FF_HAVE_GLX
+
+#ifdef FF_HAVE_OSMESA
+#include
+
+typedef struct OSMesaData
+{
+ GLData glData;
+
+ FF_LIBRARY_SYMBOL(OSMesaGetProcAddress)
+ FF_LIBRARY_SYMBOL(OSMesaCreateContext)
+ FF_LIBRARY_SYMBOL(OSMesaMakeCurrent)
+ FF_LIBRARY_SYMBOL(OSMesaDestroyContext)
+
+ OSMesaContext context;
+} OSMesaData;
+
+static const char* osMesaHandleContext(FFOpenGLResult* result, OSMesaData* data)
+{
+ unsigned char buffer[FF_OPENGL_BUFFER_WIDTH * FF_OPENGL_BUFFER_HEIGHT * sizeof(uint32_t)]; // 4 bytes per pixel (RGBA)
+
+ if(data->ffOSMesaMakeCurrent(data->context, buffer, GL_UNSIGNED_BYTE, FF_OPENGL_BUFFER_WIDTH, FF_OPENGL_BUFFER_HEIGHT) != GL_TRUE)
+ return "OSMesaMakeCurrent returned GL_FALSE";
+
+ return glHandleResult(result, &data->glData);
+}
+
+static const char* osMesaHandleData(FFOpenGLResult* result, OSMesaData* data)
+{
+ //The case to void* is required here, because OSMESAproc can't be cast to (__typeof__(data->glData.ffglGetString)) without a warning, even though it is the actual type.
+ data->glData.ffglGetString = (__typeof__(data->glData.ffglGetString)) (void*) data->ffOSMesaGetProcAddress("glGetString");
+ if(data->glData.ffglGetString == NULL)
+ return "OSMesaGetProcAddress(glGetString) returned NULL";
+
+ data->context = data->ffOSMesaCreateContext(OSMESA_RGBA, NULL);
+ if(data->context == NULL)
+ return "OSMesaCreateContext returned NULL";
+
+ const char* error = osMesaHandleContext(result, data);
+ data->ffOSMesaDestroyContext(data->context);
+ return error;
+}
+
+static const char* osMesaPrint(FFinstance* instance, FFOpenGLResult* result)
+{
+ OSMesaData data;
+
+ FF_LIBRARY_LOAD(osmesa, &instance->config.libOSMesa, "dlopen osmesa failed", "libOSMesa" FF_LIBRARY_EXTENSION, 8);
+ FF_LIBRARY_LOAD_SYMBOL_VAR_MESSAGE(osmesa, data, OSMesaGetProcAddress);
+ FF_LIBRARY_LOAD_SYMBOL_VAR_MESSAGE(osmesa, data, OSMesaCreateContext);
+ FF_LIBRARY_LOAD_SYMBOL_VAR_MESSAGE(osmesa, data, OSMesaMakeCurrent);
+ FF_LIBRARY_LOAD_SYMBOL_VAR_MESSAGE(osmesa, data, OSMesaDestroyContext);
+
+ const char* error = osMesaHandleData(result, &data);
+ dlclose(osmesa);
+ return error;
+}
+
+#endif //FF_HAVE_OSMESA
+
+const char* ffDetectOpenGL(FFinstance* instance, FFOpenGLResult* result)
+{
+ #if FF_HAVE_GL
+
+ if(instance->config.glType == FF_GL_TYPE_GLX)
+ {
+ #ifdef FF_HAVE_GLX
+ return glxPrint(instance, result);
+ #else
+ return "fastfetch was compiled without glx support";
+ #endif
+ }
+
+ if(instance->config.glType == FF_GL_TYPE_EGL)
+ {
+ #ifdef FF_HAVE_EGL
+ return eglPrint(instance, result);
+ #else
+ return "fastfetch was compiled without egl support";
+ #endif
+ }
+
+ if(instance->config.glType == FF_GL_TYPE_OSMESA)
+ {
+ #ifdef FF_HAVE_OSMESA
+ return osMesaPrint(instance, result);
+ #else
+ return "fastfetch was compiled without osmesa support";
+ #endif
+ }
+
+ const char* error = ""; // not NULL dummy value
+
+ #ifdef FF_HAVE_EGL
+ error = eglPrint(instance, result);
+ #endif
+
+ #ifdef FF_HAVE_GLX
+ if(error != NULL)
+ error = glxPrint(instance, result);
+ #endif
+
+ //We don't use osmesa in auto mode here, because it is a software implementation,
+ //that doesn't reflect the opengl supported by the hardware
+
+ return error;
+
+ #else
+
+ FF_UNUSED(instance, result);
+ return "Fastfetch was built without gl support.";
+
+ #endif //FF_HAVE_GL
+}
diff --git a/src/detection/opengl/opengl_windows.c b/src/detection/opengl/opengl_windows.c
new file mode 100644
index 000000000..92e56b974
--- /dev/null
+++ b/src/detection/opengl/opengl_windows.c
@@ -0,0 +1,108 @@
+#include "fastfetch.h"
+#include "common/printing.h"
+#include "opengl.h"
+
+#define WIN32_LEAN_AND_MEAN 1
+#include
+#include
+#ifndef GL_SHADING_LANGUAGE_VERSION // For WGL
+ #define GL_SHADING_LANGUAGE_VERSION 0x8B8C
+#endif
+
+static const char* glHandleResult(FFOpenGLResult* result)
+{
+ ffStrbufAppendS(&result->version, (const char*) glGetString(GL_VERSION));
+ ffStrbufAppendS(&result->renderer, (const char*) glGetString(GL_RENDERER));
+ ffStrbufAppendS(&result->vendor, (const char*) glGetString(GL_VENDOR));
+ ffStrbufAppendS(&result->slv, (const char*) glGetString(GL_SHADING_LANGUAGE_VERSION));
+ return NULL;
+}
+
+const char* wglHandleContext(FFOpenGLResult* result, HDC hdc, HGLRC context)
+{
+ if(wglMakeCurrent(hdc, context) == FALSE)
+ return "wglMakeCurrent() failed";
+ return glHandleResult(result);
+}
+
+const char* wglHandlePixelFormat(FFOpenGLResult* result, HWND hWnd)
+{
+ PIXELFORMATDESCRIPTOR pfd =
+ {
+ sizeof(PIXELFORMATDESCRIPTOR),
+ 1,
+ PFD_DRAW_TO_WINDOW | PFD_SUPPORT_OPENGL | PFD_DOUBLEBUFFER, //Flags
+ PFD_TYPE_RGBA, // The kind of framebuffer. RGBA or palette.
+ 32, // Colordepth of the framebuffer.
+ 0, 0, 0, 0, 0, 0,
+ 0,
+ 0,
+ 0,
+ 0, 0, 0, 0,
+ 24, // Number of bits for the depthbuffer
+ 8, // Number of bits for the stencilbuffer
+ 0, // Number of Aux buffers in the framebuffer.
+ PFD_MAIN_PLANE,
+ 0,
+ 0, 0, 0
+ };
+
+ HDC hdc = GetDC(hWnd);
+
+ if(SetPixelFormat(hdc, ChoosePixelFormat(hdc, &pfd), &pfd) == FALSE)
+ return "SetPixelFormat() failed";
+
+ HGLRC context = wglCreateContext(hdc);
+ if(context == NULL)
+ return "wglCreateContext() failed";
+
+ const char* error = wglHandleContext(result, hdc, context);
+ wglDeleteContext(context);
+
+ return error;
+}
+
+typedef struct WGLData
+{
+ FFOpenGLResult* result;
+ const char* error;
+} WGLData;
+
+LRESULT CALLBACK wglHandleWndProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam)
+{
+ switch(message)
+ {
+ case WM_CREATE: {
+ WGLData* wglData = (WGLData*)((CREATESTRUCT*)lParam)->lpCreateParams;
+ wglData->error = wglHandlePixelFormat(wglData->result, hWnd);
+ PostQuitMessage(0);
+ return 0;
+ }
+ default:
+ return DefWindowProcW(hWnd, message, wParam, lParam);
+ }
+}
+
+const char* ffDetectOpenGL(FFinstance* instance, FFOpenGLResult* result)
+{
+ FF_UNUSED(instance);
+
+ MSG msg = {0};
+ WNDCLASSW wc = {
+ .lpfnWndProc = wglHandleWndProc,
+ .hInstance = NULL,
+ .hbrBackground = (HBRUSH)COLOR_BACKGROUND,
+ .lpszClassName = L"ogl_version_check",
+ .style = CS_OWNDC,
+ };
+ if(!RegisterClassW(&wc))
+ return "RegisterClassW() failed";
+
+ WGLData data = { .result = result };
+ HWND hWnd = CreateWindowW(wc.lpszClassName, L"ogl_version_check", 0, 0, 0, 1, 1, NULL, NULL, NULL, &data);
+
+ while(GetMessageW(&msg, hWnd, 0, 0) > 0)
+ DispatchMessage(&msg);
+
+ return data.error;
+}
diff --git a/src/detection/os/os_android.c b/src/detection/os/os_android.c
index bb3b6c9da..eb4bf97d5 100644
--- a/src/detection/os/os_android.c
+++ b/src/detection/os/os_android.c
@@ -5,14 +5,11 @@ void ffDetectOSImpl(FFOSResult* os, const FFinstance* instance)
{
FF_UNUSED(instance);
- ffStrbufInit(&os->name);
- ffStrbufSetS(&os->name, "Android");
+ ffStrbufInitS(&os->name, "Android");
- ffStrbufInit(&os->prettyName);
- ffStrbufSetS(&os->prettyName, "Android");
+ ffStrbufInitS(&os->prettyName, "Android");
- ffStrbufInit(&os->id);
- ffStrbufSetS(&os->id, "android");
+ ffStrbufInitS(&os->id, "android");
ffStrbufInit(&os->version);
ffSettingsGetAndroidProperty("ro.build.version.release", &os->version);
@@ -26,13 +23,11 @@ void ffDetectOSImpl(FFOSResult* os, const FFinstance* instance)
ffStrbufInit(&os->buildID);
ffSettingsGetAndroidProperty("ro.build.id", &os->buildID);
- ffStrbufInit(&os->systemName);
- ffStrbufSetS(&os->systemName, instance->state.utsname.sysname);
+ ffStrbufInitS(&os->systemName, instance->state.utsname.sysname);
- ffStrbufInit(&os->architecture);
- ffStrbufSetS(&os->architecture, instance->state.utsname.machine);
+ ffStrbufInitS(&os->architecture, instance->state.utsname.machine);
- ffStrbufInitA(&os->idLike, 0);
- ffStrbufInitA(&os->variant, 0);
- ffStrbufInitA(&os->variantID, 0);
+ ffStrbufInit(&os->idLike);
+ ffStrbufInit(&os->variant);
+ ffStrbufInit(&os->variantID);
}
diff --git a/src/detection/os/os_windows.cpp b/src/detection/os/os_windows.cpp
new file mode 100644
index 000000000..fdda72c89
--- /dev/null
+++ b/src/detection/os/os_windows.cpp
@@ -0,0 +1,112 @@
+extern "C" {
+#include "os.h"
+#include "util/windows/unicode.h"
+}
+#include "util/windows/wmi.hpp"
+
+static const char* getOsNameByWmi(FFstrbuf* osName)
+{
+ FFWmiQuery query(L"SELECT Caption FROM Win32_OperatingSystem");
+ if(!query)
+ return "Query WMI service failed";
+
+ if(FFWmiRecord record = query.next())
+ {
+ record.getString(L"Caption", osName);
+ ffStrbufTrimRight(osName, ' ');
+ return NULL;
+ }
+
+ return "No WMI result returned";
+}
+
+static inline void wrapFreeLibrary(HMODULE* module)
+{
+ if(*module)
+ FreeLibrary(*module);
+}
+
+static const char* getOsNameByWinbrand(FFstrbuf* osName)
+{
+ //https://dennisbabkin.com/blog/?t=how-to-tell-the-real-version-of-windows-your-app-is-running-on#ver_string
+ if(HMODULE __attribute__((__cleanup__(wrapFreeLibrary))) hWinbrand = LoadLibraryW(L"winbrand.dll"))
+ {
+ auto BrandingFormatString = (PWSTR(WINAPI*)(PCWSTR))GetProcAddress(hWinbrand, "BrandingFormatString");
+ if(!BrandingFormatString)
+ return "GetProcAddress(BrandingFormatString) failed";
+
+ const wchar_t* rawName = BrandingFormatString(L"%WINDOWS_LONG%");
+ ffStrbufSetWS(osName, rawName);
+ GlobalFree((HGLOBAL)rawName);
+ return NULL;
+ }
+ return "LoadLibraryW(winbrand.dll) failed";
+}
+
+extern "C"
+void ffDetectOSImpl(FFOSResult* os, const FFinstance* instance)
+{
+ ffStrbufInit(&os->name);
+ ffStrbufInit(&os->prettyName);
+ ffStrbufInit(&os->id);
+ ffStrbufInit(&os->idLike);
+ ffStrbufInit(&os->variant);
+ ffStrbufInit(&os->variantID);
+ ffStrbufInit(&os->version);
+ ffStrbufInit(&os->versionID);
+ ffStrbufInit(&os->codename);
+ ffStrbufInit(&os->buildID);
+ ffStrbufInit(&os->systemName);
+ ffStrbufInit(&os->architecture);
+
+ if(getOsNameByWinbrand(&os->variant) && getOsNameByWmi(&os->variant))
+ return;
+
+ ffStrbufTrimRight(&os->variant, ' ');
+
+ //WMI returns the "Microsoft" prefix while BrandingFormatString doesn't. Make them consistant.
+ if(ffStrbufStartsWithS(&os->variant, "Microsoft "))
+ ffStrbufSubstrAfter(&os->variant, strlen("Microsoft ") - 1);
+
+ if(ffStrbufStartsWithS(&os->variant, "Windows "))
+ {
+ ffStrbufAppendS(&os->name, "Windows");
+ ffStrbufAppendS(&os->prettyName, "Windows");
+
+ ffStrbufSubstrAfter(&os->variant, strlen("Windows ") - 1);
+
+ if(ffStrbufStartsWithS(&os->variant, "Server "))
+ {
+ ffStrbufAppendS(&os->name, " Server");
+ ffStrbufAppendS(&os->prettyName, " Server");
+ ffStrbufSubstrAfter(&os->variant, strlen(" Server") - 1);
+ }
+
+ uint32_t index = ffStrbufFirstIndexC(&os->variant, ' ');
+ ffStrbufAppendNS(&os->version, index, os->variant.chars);
+ ffStrbufSubstrAfter(&os->variant, index);
+
+ // Windows Server 20xx Rx
+ if(ffStrbufEndsWithC(&os->prettyName, 'r'))
+ {
+ if(os->variant.chars[0] == 'R' &&
+ isdigit(os->variant.chars[1]) &&
+ (os->variant.chars[2] == '\0' || os->variant.chars[2] == ' '))
+ {
+ ffStrbufAppendF(&os->version, " R%c", os->variant.chars[1]);
+ ffStrbufSubstrAfter(&os->variant, strlen("Rx ") - 1);
+ }
+ }
+ }
+ else
+ {
+ // Unknown Windows name, please report this
+ ffStrbufAppend(&os->name, &os->variant);
+ ffStrbufClear(&os->variant);
+ }
+
+ ffStrbufAppendF(&os->id, "%*s %*s", os->prettyName.length, os->prettyName.chars, os->version.length, os->version.chars);
+
+ ffStrbufSetS(&os->architecture, instance->state.utsname.machine);
+ ffStrbufSetS(&os->systemName, instance->state.utsname.sysname);
+}
diff --git a/src/detection/packages/packages.c b/src/detection/packages/packages.c
new file mode 100644
index 000000000..2404f74e3
--- /dev/null
+++ b/src/detection/packages/packages.c
@@ -0,0 +1,19 @@
+#include "packages.h"
+#include "detection/internal.h"
+
+#include
+
+void ffDetectPackagesImpl(const FFinstance* instance, FFPackagesResult* result);
+
+const FFPackagesResult* ffDetectPackages(const FFinstance* instance)
+{
+ FF_DETECTION_INTERNAL_GUARD(FFPackagesResult,
+ memset(&result, 0, sizeof(FFPackagesResult));
+ ffStrbufInit(&result.pacmanBranch);
+
+ ffDetectPackagesImpl(instance, &result);
+
+ for(uint32_t i = 0; i < offsetof(FFPackagesResult, all) / sizeof(uint32_t); ++i)
+ result.all += ((uint32_t *)&result)[i];
+ );
+}
diff --git a/src/detection/packages/packages.h b/src/detection/packages/packages.h
new file mode 100644
index 000000000..9665b5695
--- /dev/null
+++ b/src/detection/packages/packages.h
@@ -0,0 +1,36 @@
+#pragma once
+
+#ifndef FF_INCLUDED_detection_packages_packages
+#define FF_INCLUDED_detection_packages_packages
+
+#include "fastfetch.h"
+
+typedef struct FFPackagesResult
+{
+ uint32_t apk;
+ uint32_t brew;
+ uint32_t brewCask;
+ uint32_t choco;
+ uint32_t dpkg;
+ uint32_t emerge;
+ uint32_t eopkg;
+ uint32_t flatpak;
+ uint32_t nixDefault;
+ uint32_t nixSystem;
+ uint32_t nixUser;
+ uint32_t pacman;
+ uint32_t pkg;
+ uint32_t port;
+ uint32_t rpm;
+ uint32_t scoop;
+ uint32_t snap;
+ uint32_t xbps;
+
+ uint32_t all; //Make sure this goes last
+
+ FFstrbuf pacmanBranch;
+} FFPackagesResult;
+
+const FFPackagesResult* ffDetectPackages(const FFinstance* instance);
+
+#endif
diff --git a/src/detection/packages/packages_apple.c b/src/detection/packages/packages_apple.c
new file mode 100644
index 000000000..b8bc8a682
--- /dev/null
+++ b/src/detection/packages/packages_apple.c
@@ -0,0 +1,77 @@
+#include "packages.h"
+#include "common/parsing.h"
+
+#include
+
+static uint32_t getNumElements(const char* dirname, unsigned char type)
+{
+ DIR* dirp = opendir(dirname);
+ if(dirp == NULL)
+ return 0;
+
+ uint32_t num_elements = 0;
+
+ struct dirent *entry;
+ while((entry = readdir(dirp)) != NULL) {
+ if(entry->d_type == type)
+ ++num_elements;
+ }
+
+ if(type == DT_DIR)
+ num_elements -= 2; // accounting for . and ..
+
+ closedir(dirp);
+
+ return num_elements;
+}
+
+static void countBrewPackages(const char* dirname, FFPackagesResult* result)
+{
+ FF_STRBUF_AUTO_DESTROY baseDir;
+ ffStrbufInitS(&baseDir, dirname);
+
+ uint32_t baseDirLength = baseDir.length;
+
+ ffStrbufAppendS(&baseDir, "/Caskroom");
+ result->brewCask += getNumElements(baseDir.chars, DT_DIR);
+ ffStrbufSubstrBefore(&baseDir, baseDirLength);
+
+ ffStrbufAppendS(&baseDir, "/Cellar");
+ result->brew += getNumElements(baseDir.chars, DT_DIR);
+ ffStrbufSubstrBefore(&baseDir, baseDirLength);
+}
+
+static void getBrewPackages(FFPackagesResult* result)
+{
+ const char* prefix = getenv("HOMEBREW_PREFIX");
+ if(ffStrSet(prefix))
+ return countBrewPackages(prefix, result);
+
+ countBrewPackages(FASTFETCH_TARGET_DIR_ROOT"/opt/homebrew", result);
+ countBrewPackages(FASTFETCH_TARGET_DIR_ROOT"/usr/local", result);
+}
+
+static uint32_t countMacPortsPackages(const char* dirname)
+{
+ FF_STRBUF_AUTO_DESTROY baseDir;
+ ffStrbufInitS(&baseDir, dirname);
+ ffStrbufAppendS(&baseDir, "/var/macports/software");
+
+ return getNumElements(baseDir.chars, DT_DIR);
+}
+
+static uint32_t getMacPortsPackages()
+{
+ const char* prefix = getenv("MACPORTS_PREFIX");
+ if(ffStrSet(prefix))
+ return countMacPortsPackages(prefix);
+
+ return countMacPortsPackages(FASTFETCH_TARGET_DIR_ROOT"/opt/local");
+}
+
+void ffDetectPackagesImpl(const FFinstance* instance, FFPackagesResult* result)
+{
+ FF_UNUSED(instance);
+ getBrewPackages(result);
+ result->port = getMacPortsPackages();
+}
diff --git a/src/detection/packages/packages_linux.c b/src/detection/packages/packages_linux.c
new file mode 100644
index 000000000..f4b363d54
--- /dev/null
+++ b/src/detection/packages/packages_linux.c
@@ -0,0 +1,345 @@
+#include "packages.h"
+#include "common/io.h"
+#include "common/parsing.h"
+#include "common/processing.h"
+#include "common/properties.h"
+#include "common/settings.h"
+#include "detection/os/os.h"
+
+#include
+
+static uint32_t getNumElementsImpl(const char* dirname, unsigned char type)
+{
+ DIR* dirp = opendir(dirname);
+ if(dirp == NULL)
+ return 0;
+
+ uint32_t num_elements = 0;
+
+ struct dirent *entry;
+ while((entry = readdir(dirp)) != NULL) {
+ if(entry->d_type == type)
+ ++num_elements;
+ }
+
+ if(type == DT_DIR && num_elements >= 2)
+ num_elements -= 2; // accounting for . and ..
+
+ closedir(dirp);
+
+ return num_elements;
+}
+
+static uint32_t getNumElements(FFstrbuf* baseDir, const char* dirname, unsigned char type)
+{
+ uint32_t baseDirLength = baseDir->length;
+ ffStrbufAppendS(baseDir, dirname);
+ uint32_t num_elements = getNumElementsImpl(baseDir->chars, type);
+ ffStrbufSubstrBefore(baseDir, baseDirLength);
+ return num_elements;
+}
+
+static uint32_t getNumStringsImpl(const char* filename, const char* needle)
+{
+ FILE* file = fopen(filename, "r");
+ if(file == NULL)
+ return 0;
+
+ uint32_t count = 0;
+
+ char* line = NULL;
+ size_t len = 0;
+
+ while(getline(&line, &len, file) != EOF)
+ {
+ if(strstr(line, needle) != NULL)
+ ++count;
+ }
+
+ if(line != NULL)
+ free(line);
+
+ fclose(file);
+
+ return count;
+}
+
+static uint32_t getNumStrings(FFstrbuf* baseDir, const char* filename, const char* needle)
+{
+ uint32_t baseDirLength = baseDir->length;
+ ffStrbufAppendS(baseDir, filename);
+ uint32_t num_elements = getNumStringsImpl(baseDir->chars, needle);
+ ffStrbufSubstrBefore(baseDir, baseDirLength);
+ return num_elements;
+}
+
+static uint32_t getSQLite3Int(const FFinstance* instance, FFstrbuf* baseDir, const char* dbPath, const char* query)
+{
+ uint32_t baseDirLength = baseDir->length;
+ ffStrbufAppendS(baseDir, dbPath);
+ uint32_t num_elements = (uint32_t) ffSettingsGetSQLite3Int(instance, baseDir->chars, query);
+ ffStrbufSubstrBefore(baseDir, baseDirLength);
+ return num_elements;
+}
+
+static uint32_t countFilesRecursiveImpl(FFstrbuf* baseDirPath, const char* filename)
+{
+ uint32_t baseDirPathLength = baseDirPath->length;
+
+ ffStrbufAppendC(baseDirPath, '/');
+ ffStrbufAppendS(baseDirPath, filename);
+ bool exists = ffFileExists(baseDirPath->chars, S_IFREG);
+ ffStrbufSubstrBefore(baseDirPath, baseDirPathLength);
+ if(exists)
+ return 1;
+
+ DIR* dirp = opendir(baseDirPath->chars);
+ if(dirp == NULL)
+ return 0;
+
+ ffStrbufAppendC(baseDirPath, '/');
+ baseDirPathLength = baseDirPath->length;
+
+ uint32_t sum = 0;
+
+ struct dirent *entry;
+ while((entry = readdir(dirp)) != NULL) {
+ // According to the PMS, neither category nor package name can begin with '.', so no need to check for . or .. specifically
+ if(entry->d_type != DT_DIR || entry->d_name[0] == '.')
+ continue;
+
+ ffStrbufAppendS(baseDirPath, entry->d_name);
+ sum += countFilesRecursiveImpl(baseDirPath, filename);
+ ffStrbufSubstrBefore(baseDirPath, baseDirPathLength);
+ }
+
+ closedir(dirp);
+ return sum;
+}
+
+static uint32_t countFilesRecursive(FFstrbuf* baseDir, const char* dirname, const char* filename)
+{
+ uint32_t baseDirLength = baseDir->length;
+ ffStrbufAppendS(baseDir, dirname);
+ uint32_t sum = countFilesRecursiveImpl(baseDir, filename);
+ ffStrbufSubstrBefore(baseDir, baseDirLength);
+ return sum;
+}
+
+static uint32_t getNixPackagesImpl(char* path)
+{
+ //Nix detection is kinda slow, so we only do it if the dir exists
+ if(!ffFileExists(path, S_IFDIR))
+ return 0;
+
+ FFstrbuf output;
+ ffStrbufInitA(&output, 128);
+
+ //https://github.com/LinusDierheimer/fastfetch/issues/195#issuecomment-1191748222
+ FFstrbuf command;
+ ffStrbufInitA(&command, 255);
+ ffStrbufAppendS(&command, "for x in $(nix-store --query --requisites ");
+ ffStrbufAppendS(&command, path);
+ ffStrbufAppendS(&command, "); do if [ -d $x ]; then echo $x ; fi ; done | cut -d- -f2- | egrep '([0-9]{1,}\\.)+[0-9]{1,}' | egrep -v '\\-doc$|\\-man$|\\-info$|\\-dev$|\\-bin$|^nixos-system-nixos-' | uniq");
+
+ ffProcessAppendStdOut(&output, (char* const[]) {
+ "sh",
+ "-c",
+ command.chars,
+ NULL
+ });
+
+ //Each package is a new line in the output. If at least one line is found, add 1 for the last line.
+ uint32_t result = ffStrbufCountC(&output, '\n');
+ if(result > 0)
+ result++;
+
+ ffStrbufDestroy(&output);
+ return result;
+}
+
+static uint32_t getNixPackages(FFstrbuf* baseDir, const char* dirname)
+{
+ uint32_t baseDirLength = baseDir->length;
+ ffStrbufAppendS(baseDir, dirname);
+ uint32_t num_elements = getNixPackagesImpl(baseDir->chars);
+ ffStrbufSubstrBefore(baseDir, baseDirLength);
+ return num_elements;
+}
+
+static uint32_t getXBPSImpl(FFstrbuf* baseDir)
+{
+ DIR* dir = opendir(baseDir->chars);
+ if(dir == NULL)
+ return 0;
+
+ uint32_t result = 0;
+
+ struct dirent *entry;
+ while((entry = readdir(dir)) != NULL)
+ {
+ if(entry->d_type != DT_REG || strncasecmp(entry->d_name, "pkgdb-", 6) != 0)
+ continue;
+
+ ffStrbufAppendC(baseDir, '/');
+ ffStrbufAppendS(baseDir, entry->d_name);
+ result = getNumStringsImpl(baseDir->chars, "installed");
+ break;
+ }
+
+ closedir(dir);
+ return result;
+}
+
+static uint32_t getXBPS(FFstrbuf* baseDir, const char* dirname)
+{
+ uint32_t baseDirLength = baseDir->length;
+ ffStrbufAppendS(baseDir, dirname);
+ uint32_t result = getXBPSImpl(baseDir);
+ ffStrbufSubstrBefore(baseDir, baseDirLength);
+ return result;
+}
+
+static uint32_t getSnap(FFstrbuf* baseDir)
+{
+ uint32_t result = getNumElements(baseDir, "/snap", DT_DIR);
+
+ //Accounting for the /snap/bin folder
+ return result > 0 ? result - 1 : 0;
+}
+
+#ifdef FF_HAVE_RPM
+#include "common/library.h"
+#include
+#include
+#include
+#include
+
+static uint32_t getRpmFromLibrpm(const FFinstance* instance)
+{
+ FF_LIBRARY_LOAD(rpm, &instance->config.librpm, 0, "librpm" FF_LIBRARY_EXTENSION, 12)
+ FF_LIBRARY_LOAD_SYMBOL(rpm, rpmReadConfigFiles, 0)
+ FF_LIBRARY_LOAD_SYMBOL(rpm, rpmtsCreate, 0)
+ FF_LIBRARY_LOAD_SYMBOL(rpm, rpmtsInitIterator, 0)
+ FF_LIBRARY_LOAD_SYMBOL(rpm, rpmdbGetIteratorCount, 0)
+ FF_LIBRARY_LOAD_SYMBOL(rpm, rpmdbFreeIterator, 0)
+ FF_LIBRARY_LOAD_SYMBOL(rpm, rpmtsFree, 0)
+ FF_LIBRARY_LOAD_SYMBOL(rpm, rpmlogSetMask, 0)
+
+ // Don't print any error messages
+ ffrpmlogSetMask(RPMLOG_MASK(RPMLOG_EMERG));
+
+ if(ffrpmReadConfigFiles(NULL, NULL) != 0)
+ {
+ dlclose(rpm);
+ return 0;
+ }
+
+ rpmts ts = ffrpmtsCreate();
+ if(ts == NULL)
+ {
+ dlclose(rpm);
+ return 0;
+ }
+
+ rpmdbMatchIterator mi = ffrpmtsInitIterator(ts, RPMDBI_LABEL, NULL, 0);
+ if(mi == NULL)
+ {
+ ffrpmtsFree(ts);
+ dlclose(rpm);
+ return 0;
+ }
+
+ int count = ffrpmdbGetIteratorCount(mi);
+
+ ffrpmdbFreeIterator(mi);
+ ffrpmtsFree(ts);
+ dlclose(rpm);
+
+ return count > 0 ? (uint32_t) count : 0;
+}
+
+#endif //FF_HAVE_RPM
+
+static void getPackageCounts(const FFinstance* instance, FFstrbuf* baseDir, FFPackagesResult* packageCounts)
+{
+ packageCounts->apk += getNumStrings(baseDir, "/lib/apk/db/installed", "C:Q");
+ packageCounts->dpkg += getNumStrings(baseDir, "/var/lib/dpkg/status", "Status: ");
+ packageCounts->emerge += countFilesRecursive(baseDir, "/var/db/pkg", "SIZE");
+ packageCounts->eopkg += getNumElements(baseDir, "/var/lib/eopkg/package", DT_DIR);
+ packageCounts->flatpak += getNumElements(baseDir, "/var/lib/flatpak/app", DT_DIR);
+ packageCounts->nixDefault += getNixPackages(baseDir, "/nix/var/nix/profiles/default");
+ packageCounts->nixSystem += getNixPackages(baseDir, "/run/current-system");
+ packageCounts->pacman += getNumElements(baseDir, "/var/lib/pacman/local", DT_DIR);
+ packageCounts->pkg += getSQLite3Int(instance, baseDir, "/var/db/pkg/local.sqlite", "SELECT count(id) FROM packages");
+ packageCounts->rpm += getSQLite3Int(instance, baseDir, "/var/lib/rpm/rmpdb.sqlite", "SELECT count(blob) FROM Packages");
+ packageCounts->snap += getSnap(baseDir);
+ packageCounts->xbps += getXBPS(baseDir, "/var/db/xbps");
+}
+
+static void getPackageCountsRegular(const FFinstance* instance, FFstrbuf* baseDir, FFPackagesResult* packageCounts)
+{
+ getPackageCounts(instance, baseDir, packageCounts);
+
+ uint32_t baseDirLength = baseDir->length;
+ ffStrbufAppendS(baseDir, FASTFETCH_TARGET_DIR_ETC"/pacman-mirrors.conf");
+ if(ffParsePropFile(baseDir->chars, "Branch =", &packageCounts->pacmanBranch) && packageCounts->pacmanBranch.length == 0)
+ ffStrbufAppendS(&packageCounts->pacmanBranch, "stable");
+ ffStrbufSubstrBefore(baseDir, baseDirLength);
+}
+
+static void getPackageCountsBedrock(const FFinstance* instance, FFstrbuf* baseDir, FFPackagesResult* packageCounts)
+{
+ uint32_t baseDirLength = baseDir->length;
+
+ ffStrbufAppendS(baseDir, "/bedrock/strata");
+
+ DIR* dir = opendir(baseDir->chars);
+ if(dir == NULL)
+ {
+ ffStrbufSubstrBefore(baseDir, baseDirLength);
+ return;
+ }
+
+ ffStrbufAppendC(baseDir, '/');
+ uint32_t baseDirLength2 = baseDir->length;
+
+ struct dirent* entry;
+ while((entry = readdir(dir)) != NULL)
+ {
+ if(entry->d_type != DT_DIR)
+ continue;
+
+ ffStrbufAppendS(baseDir, entry->d_name);
+ getPackageCounts(instance, baseDir, packageCounts);
+ ffStrbufSubstrBefore(baseDir, baseDirLength2);
+ }
+
+ closedir(dir);
+ ffStrbufSubstrBefore(baseDir, baseDirLength);
+}
+
+void ffDetectPackagesImpl(const FFinstance* instance, FFPackagesResult* result)
+{
+ FFstrbuf baseDir;
+ ffStrbufInitA(&baseDir, 512);
+ ffStrbufAppendS(&baseDir, FASTFETCH_TARGET_DIR_ROOT);
+
+ if(ffStrbufIgnCaseCompS(&(ffDetectOS(instance)->id), "bedrock") == 0)
+ getPackageCountsBedrock(instance, &baseDir, result);
+ else
+ getPackageCountsRegular(instance, &baseDir, result);
+
+ // If SQL failed, we can still try with librpm.
+ // This is needed on openSUSE, which seems to use a proprietary database file
+ // This method doesn't work on bedrock, so we do it here.
+ #ifdef FF_HAVE_RPM
+ if(result->rpm == 0)
+ result->rpm = getRpmFromLibrpm(instance);
+ #endif
+
+ ffStrbufSetS(&baseDir, instance->state.passwd->pw_dir);
+ result->nixUser = getNixPackages(&baseDir, "/.nix-profile");
+
+ ffStrbufDestroy(&baseDir);
+}
diff --git a/src/detection/packages/packages_windows.c b/src/detection/packages/packages_windows.c
new file mode 100644
index 000000000..93739e9bf
--- /dev/null
+++ b/src/detection/packages/packages_windows.c
@@ -0,0 +1,58 @@
+#include "packages.h"
+
+#include
+#include
+
+static uint32_t getNumElements(const char* searchPath /* including `\*` suffix */, DWORD type)
+{
+ uint32_t counter = 0;
+ WIN32_FIND_DATAA wfd;
+ HANDLE hFind = FindFirstFileA(searchPath, &wfd);
+
+ if (hFind != INVALID_HANDLE_VALUE)
+ {
+ do // Managed to locate and create an handle to that folder.
+ {
+ if(wfd.dwFileAttributes & type)
+ counter++;
+ } while (FindNextFileA(hFind, &wfd));
+ FindClose(hFind);
+ }
+
+ if(type == FILE_ATTRIBUTE_DIRECTORY && counter >= 2)
+ counter -= 2; // accounting for . and ..
+
+ return counter;
+}
+
+static void detectScoop(const FFinstance* instance, FFPackagesResult* result)
+{
+ char scoopPath[MAX_PATH + 3];
+ strcpy(scoopPath, instance->state.passwd->pw_dir);
+ strncat(scoopPath, "/scoop/apps/*", sizeof(scoopPath) - 1 - strlen(scoopPath));
+ result->scoop = getNumElements(scoopPath, FILE_ATTRIBUTE_DIRECTORY);
+ if(result->scoop > 0)
+ result->scoop--; // scoop
+}
+
+static void detectChoco(const FFinstance* instance, FFPackagesResult* result)
+{
+ FF_UNUSED(instance);
+
+ const char* chocoInstall = getenv("ChocolateyInstall");
+ if(!chocoInstall || chocoInstall[0] == '\0')
+ return;
+
+ char chocoPath[MAX_PATH + 3];
+ strcpy(chocoPath, chocoInstall);
+ strncat(chocoPath, "/lib/*", sizeof(chocoPath) - 1 - strlen(chocoPath));
+ result->choco = getNumElements(chocoPath, FILE_ATTRIBUTE_DIRECTORY);
+ if(result->choco > 0)
+ result->choco--; // choco
+}
+
+void ffDetectPackagesImpl(const FFinstance* instance, FFPackagesResult* result)
+{
+ detectScoop(instance, result);
+ detectChoco(instance, result);
+}
diff --git a/src/detection/poweradapter/poweradapter_linux.c b/src/detection/poweradapter/poweradapter_nosupport.c
similarity index 75%
rename from src/detection/poweradapter/poweradapter_linux.c
rename to src/detection/poweradapter/poweradapter_nosupport.c
index 4e5bebd80..cf6d1c841 100644
--- a/src/detection/poweradapter/poweradapter_linux.c
+++ b/src/detection/poweradapter/poweradapter_nosupport.c
@@ -3,5 +3,5 @@
const char* ffDetectPowerAdapterImpl(FFinstance* instance, FFlist* results)
{
FF_UNUSED(instance, results);
- return "Unimplemented";
+ return "Not supported on this platform";
}
diff --git a/src/detection/processes/processes.h b/src/detection/processes/processes.h
new file mode 100644
index 000000000..74b29e1bb
--- /dev/null
+++ b/src/detection/processes/processes.h
@@ -0,0 +1,10 @@
+#pragma once
+
+#ifndef FF_INCLUDED_detection_processes_processes
+#define FF_INCLUDED_detection_processes_processes
+
+#include "fastfetch.h"
+
+uint32_t ffDetectProcesses(FFstrbuf* error);
+
+#endif
diff --git a/src/detection/processes/processes_bsd.c b/src/detection/processes/processes_bsd.c
new file mode 100644
index 000000000..36bb14773
--- /dev/null
+++ b/src/detection/processes/processes_bsd.c
@@ -0,0 +1,20 @@
+#include "processes.h"
+
+#include
+#ifdef __FreeBSD__
+ #include
+ #include
+#endif
+
+uint32_t ffDetectProcesses(FFstrbuf* error)
+{
+ int request[] = {CTL_KERN, KERN_PROC, KERN_PROC_ALL};
+ size_t length;
+
+ if(sysctl(request, sizeof(request) / sizeof(*request), NULL, &length, NULL, 0) != 0)
+ {
+ ffStrbufAppendS(error, "sysctl() failed");
+ return 0;
+ }
+ return (uint32_t)(length / sizeof(struct kinfo_proc));
+}
diff --git a/src/detection/processes/processes_linux.c b/src/detection/processes/processes_linux.c
new file mode 100644
index 000000000..91871f867
--- /dev/null
+++ b/src/detection/processes/processes_linux.c
@@ -0,0 +1,11 @@
+#include "processes.h"
+
+#include
+
+uint32_t ffDetectProcesses(FFstrbuf* error)
+{
+ struct sysinfo info;
+ if(sysinfo(&info) != 0)
+ ffStrbufAppendS(error, "sysinfo() failed");
+ return (uint32_t) info.procs;
+}
diff --git a/src/detection/processes/processes_windows.cpp b/src/detection/processes/processes_windows.cpp
new file mode 100644
index 000000000..69a494629
--- /dev/null
+++ b/src/detection/processes/processes_windows.cpp
@@ -0,0 +1,64 @@
+extern "C" {
+#include "processes.h"
+#include "util/mallocHelper.h"
+}
+
+#ifdef FF_USE_WIN_NTAPI
+
+#include
+#include
+
+uint32_t ffDetectProcesses(FFstrbuf* error)
+{
+ ULONG size = 0;
+ if(NtQuerySystemInformation(SystemProcessInformation, nullptr, 0, &size) != STATUS_INFO_LENGTH_MISMATCH)
+ {
+ ffStrbufAppendS(error, "NtQuerySystemInformation(SystemProcessInformation, NULL) failed");
+ return 0;
+ }
+ size += sizeof(SystemProcessInformation) * 5; //What if new processes are created during two syscalls?
+
+ SYSTEM_PROCESS_INFORMATION* FF_AUTO_FREE pstart = (SYSTEM_PROCESS_INFORMATION*)malloc(size);
+ if(!pstart)
+ {
+ ffStrbufAppendF(error, "malloc(%u) failed", (unsigned)size);
+ return 0;
+ }
+
+ if(!NT_SUCCESS(NtQuerySystemInformation(SystemProcessInformation, pstart, size, nullptr)))
+ {
+ ffStrbufAppendS(error, "NtQuerySystemInformation(SystemProcessInformation, pstart) failed");
+ return 0;
+ }
+
+ uint32_t result = 1; //Init with 1 because we test for ptr->NextEntryOffset
+ for (auto ptr = pstart; ptr->NextEntryOffset; ptr = (SYSTEM_PROCESS_INFORMATION*)((uint8_t*)ptr + ptr->NextEntryOffset))
+ ++result;
+
+ return result;
+}
+
+#else
+
+#include "util/windows/wmi.hpp"
+
+uint32_t ffDetectProcesses(FFstrbuf* error)
+{
+ FFWmiQuery query(L"SELECT NumberOfProcesses FROM Win32_OperatingSystem", error);
+ if(!query)
+ return 0;
+
+ if(FFWmiRecord record = query.next())
+ {
+ uint64_t result = 0;
+ record.getUnsigned(L"NumberOfProcesses", &result);
+ return (uint32_t)result;
+ }
+ else
+ {
+ ffStrbufAppendS(error, "No Wmi result returned");
+ return 0;
+ }
+}
+
+#endif
diff --git a/src/detection/qt.c b/src/detection/qt.c
index 5bd9b2403..6a7794c8e 100644
--- a/src/detection/qt.c
+++ b/src/detection/qt.c
@@ -1,11 +1,11 @@
#include "fastfetch.h"
#include "detection/qt.h"
#include "common/properties.h"
+#include "common/thread.h"
#include "detection/displayserver/displayserver.h"
#include
#include
-#include
static inline bool allValuesSet(const FFQtResult* result)
{
@@ -135,12 +135,12 @@ const FFQtResult* ffDetectQt(const FFinstance* instance)
{
static FFQtResult result;
- static pthread_mutex_t mutex = PTHREAD_MUTEX_INITIALIZER;
+ static FFThreadMutex mutex = FF_THREAD_MUTEX_INITIALIZER;
static bool init = false;
- pthread_mutex_lock(&mutex);
+ ffThreadMutexLock(&mutex);
if(init)
{
- pthread_mutex_unlock(&mutex);
+ ffThreadMutexUnlock(&mutex);
return &result;
}
init = true;
@@ -157,6 +157,6 @@ const FFQtResult* ffDetectQt(const FFinstance* instance)
else if(ffStrbufIgnCaseCompS(&wmde->dePrettyName, "LXQt") == 0)
detectLXQt(instance, &result);
- pthread_mutex_unlock(&mutex);
+ ffThreadMutexUnlock(&mutex);
return &result;
}
diff --git a/src/detection/storage.h b/src/detection/storage.h
new file mode 100644
index 000000000..c8cf7ed0d
--- /dev/null
+++ b/src/detection/storage.h
@@ -0,0 +1,15 @@
+#pragma once
+
+#ifndef FF_INCLUDED_detection_storage
+#define FF_INCLUDED_detection_storage
+
+#include "fastfetch.h"
+
+typedef struct FFMemoryStorage
+{
+ FFstrbuf error;
+ uint64_t bytesUsed;
+ uint64_t bytesTotal;
+} FFMemoryStorage;
+
+#endif
diff --git a/src/detection/swap/swap.h b/src/detection/swap/swap.h
new file mode 100644
index 000000000..246b2bfa3
--- /dev/null
+++ b/src/detection/swap/swap.h
@@ -0,0 +1,10 @@
+#pragma once
+
+#ifndef FF_INCLUDED_detection_swap_swap
+#define FF_INCLUDED_detection_swap_swap
+
+#include "detection/storage.h"
+
+void ffDetectSwap(FFMemoryStorage* result);
+
+#endif
diff --git a/src/detection/swap/swap_apple.c b/src/detection/swap/swap_apple.c
new file mode 100644
index 000000000..34e72802a
--- /dev/null
+++ b/src/detection/swap/swap_apple.c
@@ -0,0 +1,18 @@
+#include "swap.h"
+#include "common/sysctl.h"
+
+#include
+
+void ffDetectSwap(FFMemoryStorage* swap)
+{
+ struct xsw_usage xsw;
+ size_t size = sizeof(xsw);
+ if(sysctlbyname("vm.swapusage", &xsw, &size, 0, 0) != 0)
+ {
+ ffStrbufAppendS(&swap->error, "Failed to read vm.swapusage");
+ return;
+ }
+
+ swap->bytesTotal = xsw.xsu_total;
+ swap->bytesUsed = xsw.xsu_used;
+}
diff --git a/src/detection/swap/swap_bsd.c b/src/detection/swap/swap_bsd.c
new file mode 100644
index 000000000..b4c5fa7c7
--- /dev/null
+++ b/src/detection/swap/swap_bsd.c
@@ -0,0 +1,8 @@
+#include "swap.h"
+#include "common/sysctl.h"
+
+void ffDetectSwap(FFMemoryStorage* swap)
+{
+ swap->bytesTotal = (uint64_t)ffSysctlGetInt64("vm.swap_total", 0);
+ swap->bytesUsed = (uint64_t)ffSysctlGetInt64("vm.swap_reserved", 0);
+}
diff --git a/src/detection/swap/swap_linux.c b/src/detection/swap/swap_linux.c
new file mode 100644
index 000000000..ee623c0c4
--- /dev/null
+++ b/src/detection/swap/swap_linux.c
@@ -0,0 +1,13 @@
+#include "swap.h"
+
+#include
+
+void ffDetectSwap(FFMemoryStorage* swap)
+{
+ struct sysinfo info;
+ if(sysinfo(&info) != 0)
+ ffStrbufAppendS(&swap->error, "sysinfo() failed");
+
+ swap->bytesTotal = info.totalswap * (uint64_t) info.mem_unit;
+ swap->bytesUsed = (info.totalswap - info.freeswap) * (uint64_t) info.mem_unit;
+}
diff --git a/src/detection/swap/swap_windows.cpp b/src/detection/swap/swap_windows.cpp
new file mode 100644
index 000000000..224a3ce35
--- /dev/null
+++ b/src/detection/swap/swap_windows.cpp
@@ -0,0 +1,64 @@
+extern "C" {
+#include "swap.h"
+#include "util/mallocHelper.h"
+}
+
+#ifdef FF_USE_WIN_NTAPI
+
+#include
+#include