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 + +extern "C" +void ffDetectSwap(FFMemoryStorage* swap) +{ + SYSTEM_INFO sysInfo; + GetNativeSystemInfo(&sysInfo); + + ULONG size = sizeof(SYSTEM_PAGEFILE_INFORMATION); + SYSTEM_PAGEFILE_INFORMATION* FF_AUTO_FREE pstart = (SYSTEM_PAGEFILE_INFORMATION*)malloc(size); + while(true) + { + NTSTATUS status = NtQuerySystemInformation(SystemPagefileInformation, pstart, size, &size); + if(status == STATUS_INFO_LENGTH_MISMATCH) + { + if(!(pstart = (SYSTEM_PAGEFILE_INFORMATION*)realloc(pstart, size))) + { + ffStrbufAppendF(&swap->error, "relloc(pstart, %lu) failed", size); + return; + } + } + else if(!NT_SUCCESS(status)) + { + ffStrbufAppendF(&swap->error, "NtQuerySystemInformation(SystemPagefileInformation, %lu) failed", size); + return; + } + break; + } + swap->bytesUsed = (uint64_t)pstart->TotalUsed * sysInfo.dwPageSize; + swap->bytesTotal = (uint64_t)pstart->CurrentSize * sysInfo.dwPageSize; +} + +#else + +#include "util/windows/wmi.hpp" + +extern "C" +void ffDetectSwapImpl(FFMemoryStorage* swap) +{ + FFWmiQuery query(L"SELECT AllocatedBaseSize, CurrentUsage FROM Win32_PageFileUsage", &swap->error); + if(!query) + return; + + if(FFWmiRecord record = query.next()) + { + //MB + record.getUnsigned(L"AllocatedBaseSize", &swap->bytesTotal); + record.getUnsigned(L"CurrentUsage", &swap->bytesUsed); + swap->bytesTotal *= 1024 * 1024; + swap->bytesUsed *= 1024 * 1024; + } + else + ffStrbufInitS(&swap->error, "No Wmi result returned"); +} + +#endif diff --git a/src/detection/temps/temps_apple.c b/src/detection/temps/temps_apple.c index b952e720b..9daee1c91 100644 --- a/src/detection/temps/temps_apple.c +++ b/src/detection/temps/temps_apple.c @@ -104,12 +104,11 @@ static uint32_t smcStrtoul(const char *str, int size, int base) static void smcUltostr(char *str, uint32_t val) { - str[0] = '\0'; - sprintf(str, "%c%c%c%c", - (unsigned int)val >> 24, - (unsigned int)val >> 16, - (unsigned int)val >> 8, - (unsigned int)val); + str[0] = (char)(val >> 24); + str[1] = (char)(val >> 16); + str[2] = (char)(val >> 8); + str[3] = (char)val; + str[4] = '\0'; } static const char *smcCall(io_connect_t conn, uint32_t selector, SmcKeyData_t *inputStructure, SmcKeyData_t *outputStructure) @@ -207,7 +206,7 @@ static const char *smcReadValue(io_connect_t conn, const UInt32Char_t key, doubl uint64_t tmp = 0; for (uint32_t i = 0; i < val.dataSize; i++) tmp += (uint64_t)((uint8_t)(val.bytes[i]) * pow(256, val.dataSize - 1 - i)); - *value = tmp; + *value = (double)tmp; } else if (strcmp(val.dataType, kDataTypeFlt) == 0) { diff --git a/src/detection/temps/temps_linux.c b/src/detection/temps/temps_linux.c index 5fad783d5..cb342a9f8 100644 --- a/src/detection/temps/temps_linux.c +++ b/src/detection/temps/temps_linux.c @@ -1,9 +1,9 @@ #include "fastfetch.h" #include "common/io.h" +#include "common/thread.h" #include "temps_linux.h" #include -#include #include static bool parseHwmonDir(FFstrbuf* dir, FFTempValue* value) @@ -38,13 +38,13 @@ static bool parseHwmonDir(FFstrbuf* dir, FFTempValue* value) const FFTempsResult* ffDetectTemps(const FFinstance* instance) { static FFTempsResult 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; @@ -52,7 +52,7 @@ const FFTempsResult* ffDetectTemps(const FFinstance* instance) if(!instance->config.allowSlowOperations) { ffListInitA(&result.values, sizeof(FFTempValue), 0); - pthread_mutex_unlock(&mutex); + ffThreadMutexUnlock(&mutex); return &result; } @@ -68,7 +68,7 @@ const FFTempsResult* ffDetectTemps(const FFinstance* instance) if(dirp == NULL) { ffStrbufDestroy(&baseDir); - pthread_mutex_unlock(&mutex); + ffThreadMutexUnlock(&mutex); return &result; } @@ -97,6 +97,6 @@ const FFTempsResult* ffDetectTemps(const FFinstance* instance) closedir(dirp); ffStrbufDestroy(&baseDir); - pthread_mutex_unlock(&mutex); + ffThreadMutexUnlock(&mutex); return &result; } diff --git a/src/detection/terminalShell.c b/src/detection/terminalShell.c deleted file mode 100644 index 9ab5ba1eb..000000000 --- a/src/detection/terminalShell.c +++ /dev/null @@ -1,305 +0,0 @@ -#include "fastfetch.h" -#include "detection/terminalshell.h" -#include "common/io.h" -#include "common/parsing.h" -#include "common/processing.h" - -#include -#include -#include -#include -#include - -static void setExeName(FFstrbuf* exe, const char** exeName) -{ - uint32_t lastSlashIndex = ffStrbufLastIndexC(exe, '/'); - if(lastSlashIndex < exe->length) - *exeName = exe->chars + lastSlashIndex + 1; -} - -static void getProcessInformation(const char* pid, FFstrbuf* processName, FFstrbuf* exe, const char** exeName) -{ - FFstrbuf cmdlineFilePath; - ffStrbufInit(&cmdlineFilePath); - ffStrbufAppendS(&cmdlineFilePath, "/proc/"); - ffStrbufAppendS(&cmdlineFilePath, pid); - ffStrbufAppendS(&cmdlineFilePath, "/cmdline"); - - ffReadFileBuffer(cmdlineFilePath.chars, exe); - ffStrbufSubstrBeforeFirstC(exe, '\0'); //Trim the arguments - ffStrbufTrimLeft(exe, '-'); //Happens in TTY - - if(exe->length == 0) - ffStrbufSet(exe, processName); - - setExeName(exe, exeName); - - ffStrbufDestroy(&cmdlineFilePath); -} - -static void getTerminalShell(FFTerminalShellResult* result, const char* pid) -{ - FFstrbuf statFilePath; - ffStrbufInit(&statFilePath); - ffStrbufAppendS(&statFilePath, "/proc/"); - ffStrbufAppendS(&statFilePath, pid); - ffStrbufAppendS(&statFilePath, "/stat"); - - FILE* stat = fopen(statFilePath.chars, "r"); - - ffStrbufDestroy(&statFilePath); - - if(stat == NULL) - return; - - char name[256]; - name[0] = '\0'; - - char ppid[256]; - ppid[0] = '\0'; - - if( - fscanf(stat, "%*s (%255[^)]) %*c %255s", name, ppid) != 2 || //stat (comm) state ppid - !ffStrSet(name) || - !ffStrSet(ppid) || - *ppid == '-' || - strcasecmp(ppid, "0") == 0 - ) { - fclose(stat); - return; - } - - fclose(stat); - - //Common programs that are between terminal and own process, but are not the shell - if( - strcasecmp(name, "sudo") == 0 || - strcasecmp(name, "su") == 0 || - strcasecmp(name, "doas") == 0 || - strcasecmp(name, "strace") == 0 || - strcasecmp(name, "sshd") == 0 || - strcasecmp(name, "gdb") == 0 || - strcasecmp(name, "guake-wrapped") == 0 - ) { - getTerminalShell(result, ppid); - return; - } - - //Known shells - if( - strcasecmp(name, "bash") == 0 || - strcasecmp(name, "sh") == 0 || - strcasecmp(name, "zsh") == 0 || - strcasecmp(name, "ksh") == 0 || - strcasecmp(name, "fish") == 0 || - strcasecmp(name, "dash") == 0 || - strcasecmp(name, "git-shell") == 0 - ) { - ffStrbufSetS(&result->shellProcessName, name); // prevent from `fishbash` - getProcessInformation(pid, &result->shellProcessName, &result->shellExe, &result->shellExeName); - - getTerminalShell(result, ppid); - return; - } - - ffStrbufAppendS(&result->terminalProcessName, name); - getProcessInformation(pid, &result->terminalProcessName, &result->terminalExe, &result->terminalExeName); -} - -static void getTerminalFromEnv(FFTerminalShellResult* result) -{ - if( - result->terminalProcessName.length > 0 && - !ffStrbufStartsWithIgnCaseS(&result->terminalProcessName, "login") && - ffStrbufIgnCaseCompS(&result->terminalProcessName, "(login)") != 0 && - ffStrbufIgnCaseCompS(&result->terminalProcessName, "systemd") != 0 && - ffStrbufIgnCaseCompS(&result->terminalProcessName, "init") != 0 && - ffStrbufIgnCaseCompS(&result->terminalProcessName, "(init)") != 0 && - ffStrbufIgnCaseCompS(&result->terminalProcessName, "0") != 0 - ) return; - - char* term = NULL; - - //SSH - if(getenv("SSH_CONNECTION") != NULL) - term = getenv("SSH_TTY"); - - //Windows Terminal - if(!ffStrSet(term) && ( - getenv("WT_SESSION") != NULL || - getenv("WT_PROFILE_ID") != NULL - )) term = "Windows Terminal"; - - //Alacritty - if(!ffStrSet(term) && ( - getenv("ALACRITTY_SOCKET") != NULL || - getenv("ALACRITTY_LOG") != NULL || - getenv("ALACRITTY_WINDOW_ID") != NULL - )) term = "Alacritty"; - - //Termux - if(!ffStrSet(term) && ( - getenv("TERMUX_VERSION") != NULL || - getenv("TERMUX_MAIN_PACKAGE_FORMAT") != NULL || - getenv("TMUX_TMPDIR") != NULL - )) term = "Termux"; - - //Konsole - if(!ffStrSet(term) && ( - getenv("KONSOLE_VERSION") != NULL - )) term = "konsole"; - - //MacOS - if(!ffStrSet(term)) - term = getenv("TERM_PROGRAM"); - - //We are in WSL but not in Windows Terminal - if(!ffStrSet(term) && ( - getenv("WSLENV") != NULL || - getenv("WSL_DISTRO") != NULL || - getenv("WSL_INTEROP") != NULL - )) - term = "conhost"; - - //Normal Terminal - if(!ffStrSet(term)) - term = getenv("TERM"); - - //TTY - if(!ffStrSet(term) || strcasecmp(term, "linux") == 0) - term = ttyname(STDIN_FILENO); - - ffStrbufSetS(&result->terminalProcessName, term); - ffStrbufSetS(&result->terminalExe, term); - setExeName(&result->terminalExe, &result->terminalExeName); -} - -static void getUserShellFromEnv(FFTerminalShellResult* result) -{ - ffStrbufAppendS(&result->userShellExe, getenv("SHELL")); - setExeName(&result->userShellExe, &result->userShellExeName); - - //If shell detection via processes failed - if(result->shellProcessName.length == 0 && result->userShellExe.length > 0) - { - ffStrbufAppendS(&result->shellProcessName, result->userShellExeName); - ffStrbufSet(&result->shellExe, &result->userShellExe); - setExeName(&result->shellExe, &result->shellExeName); - } -} - -static void getShellVersionBash(FFstrbuf* exe, FFstrbuf* version) -{ - ffProcessAppendStdOut(version, (char* const[]) { - "env", - "-i", - exe->chars, - "--norc", - "--noprofile", - "-c", - "printf \"%s\" \"$BASH_VERSION\"", - NULL - }); - ffStrbufSubstrBeforeFirstC(version, '('); -} - -static void getShellVersionZsh(FFstrbuf* exe, FFstrbuf* version) -{ - ffProcessAppendStdOut(version, (char* const[]) { - exe->chars, - "--version", - NULL - }); - ffStrbufSubstrBeforeLastC(version, ' '); - ffStrbufSubstrAfterFirstC(version, ' '); -} - -static void getShellVersionFish(FFstrbuf* exe, FFstrbuf* version) -{ - ffProcessAppendStdOut(version, (char* const[]) { - exe->chars, - "--version", - NULL - }); - ffStrbufTrimRight(version, '\n'); - ffStrbufSubstrAfterLastC(version, ' '); -} - -static void getShellVersionGeneric(FFstrbuf* exe, const char* exeName, FFstrbuf* version) -{ - FFstrbuf command; - ffStrbufInit(&command); - ffStrbufAppendS(&command, "printf \"%s\" \"$"); - ffStrbufAppendTransformS(&command, exeName, toupper); - ffStrbufAppendS(&command, "_VERSION\""); - - ffProcessAppendStdOut(version, (char* const[]) { - "env", - "-i", - exe->chars, - "-c", - command.chars, - NULL - }); - ffStrbufSubstrBeforeFirstC(version, '('); - ffStrbufRemoveStrings(version, 2, "-release", "release"); - - ffStrbufDestroy(&command); -} - -static void getShellVersion(FFstrbuf* exe, const char* exeName, FFstrbuf* version) -{ - if(strcasecmp(exeName, "bash") == 0) - getShellVersionBash(exe, version); - else if(strcasecmp(exeName, "zsh") == 0) - getShellVersionZsh(exe, version); - else if(strcasecmp(exeName, "fish") == 0) - getShellVersionFish(exe, version); - else - getShellVersionGeneric(exe, exeName, version); -} - -const FFTerminalShellResult* ffDetectTerminalShell(const FFinstance* instance) -{ - FF_UNUSED(instance); - - static pthread_mutex_t mutex = PTHREAD_MUTEX_INITIALIZER; - static FFTerminalShellResult result; - static bool init = false; - pthread_mutex_lock(&mutex); - if(init) - { - pthread_mutex_unlock(&mutex); - return &result; - } - init = true; - - ffStrbufInit(&result.shellProcessName); - ffStrbufInitA(&result.shellExe, 128); - result.shellExeName = result.shellExe.chars; - ffStrbufInit(&result.shellVersion); - - ffStrbufInit(&result.terminalProcessName); - ffStrbufInitA(&result.terminalExe, 128); - result.terminalExeName = result.terminalExe.chars; - - ffStrbufInit(&result.userShellExe); - result.userShellExeName = result.userShellExe.chars; - ffStrbufInit(&result.userShellVersion); - - char ppid[32]; - snprintf(ppid, sizeof(ppid) - 1, "%i", getppid()); - getTerminalShell(&result, ppid); - - getTerminalFromEnv(&result); - getUserShellFromEnv(&result); - getShellVersion(&result.shellExe, result.shellExeName, &result.shellVersion); - - if(strcasecmp(result.shellExeName, result.userShellExeName) != 0) - getShellVersion(&result.userShellExe, result.userShellExeName, &result.userShellVersion); - else - ffStrbufSet(&result.userShellVersion, &result.shellVersion); - - pthread_mutex_unlock(&mutex); - return &result; -} diff --git a/src/detection/terminalfont/terminalfont.c b/src/detection/terminalfont/terminalfont.c index 2ecb332df..5716b720d 100644 --- a/src/detection/terminalfont/terminalfont.c +++ b/src/detection/terminalfont/terminalfont.c @@ -2,7 +2,7 @@ #include "common/properties.h" #include "common/processing.h" #include "detection/internal.h" -#include "detection/terminalshell.h" +#include "detection/terminalshell/terminalshell.h" static void detectAlacritty(const FFinstance* instance, FFTerminalFontResult* terminalFont) { FFstrbuf fontName; @@ -64,8 +64,286 @@ static void detectTTY(FFTerminalFontResult* terminalFont) ffStrbufDestroy(&fontName); } +#if defined(_WIN32) || defined(__linux__) +#ifdef FF_HAVE_LIBCJSON + +#include "common/library.h" +#include "common/processing.h" + +#include +#include + +typedef struct CJSONData +{ + FF_LIBRARY_SYMBOL(cJSON_Parse) + FF_LIBRARY_SYMBOL(cJSON_IsObject) + FF_LIBRARY_SYMBOL(cJSON_GetObjectItemCaseSensitive) + FF_LIBRARY_SYMBOL(cJSON_IsString) + FF_LIBRARY_SYMBOL(cJSON_GetStringValue) + FF_LIBRARY_SYMBOL(cJSON_IsNumber) + FF_LIBRARY_SYMBOL(cJSON_GetNumberValue) + FF_LIBRARY_SYMBOL(cJSON_IsArray) + FF_LIBRARY_SYMBOL(cJSON_Delete) +} CJSONData; + +static const char* detectWTProfile(CJSONData* cjsonData, cJSON* profile, FFstrbuf* name, int* size) +{ + if(!cjsonData->ffcJSON_IsObject(profile)) + return "cJSON_IsObject(profile) returns false"; + + cJSON* font = cjsonData->ffcJSON_GetObjectItemCaseSensitive(profile, "font"); + if(!cjsonData->ffcJSON_IsObject(font)) + return "cJSON_IsObject(font) returns false"; + + if(name->length == 0) + { + cJSON* pface = cjsonData->ffcJSON_GetObjectItemCaseSensitive(font, "face"); + if(cjsonData->ffcJSON_IsString(pface)) + ffStrbufAppendS(name, cjsonData->ffcJSON_GetStringValue(pface)); + } + if(*size < 0) + { + cJSON* psize = cjsonData->ffcJSON_GetObjectItemCaseSensitive(font, "size"); + if(cjsonData->ffcJSON_IsNumber(psize)) + *size = (int)cjsonData->ffcJSON_GetNumberValue(psize); + } + return NULL; +} + +static const char* detectFromWTImpl(const FFinstance* instance, FFstrbuf* content, FFstrbuf* name, int* size) +{ + CJSONData cjsonData; + + FF_LIBRARY_LOAD(libcjson, &instance->config.libcJSON, "dlopen libcjson"FF_LIBRARY_EXTENSION" failed", "libcjson"FF_LIBRARY_EXTENSION, 1) + FF_LIBRARY_LOAD_SYMBOL_VAR_MESSAGE(libcjson, cjsonData, cJSON_Parse) + FF_LIBRARY_LOAD_SYMBOL_VAR_MESSAGE(libcjson, cjsonData, cJSON_IsObject) + FF_LIBRARY_LOAD_SYMBOL_VAR_MESSAGE(libcjson, cjsonData, cJSON_GetObjectItemCaseSensitive) + FF_LIBRARY_LOAD_SYMBOL_VAR_MESSAGE(libcjson, cjsonData, cJSON_IsString) + FF_LIBRARY_LOAD_SYMBOL_VAR_MESSAGE(libcjson, cjsonData, cJSON_GetStringValue) + FF_LIBRARY_LOAD_SYMBOL_VAR_MESSAGE(libcjson, cjsonData, cJSON_IsNumber) + FF_LIBRARY_LOAD_SYMBOL_VAR_MESSAGE(libcjson, cjsonData, cJSON_GetNumberValue) + FF_LIBRARY_LOAD_SYMBOL_VAR_MESSAGE(libcjson, cjsonData, cJSON_IsArray) + FF_LIBRARY_LOAD_SYMBOL_VAR_MESSAGE(libcjson, cjsonData, cJSON_Delete) + + const char* error = NULL; + + cJSON* root = cjsonData.ffcJSON_Parse(content->chars); + if(!cjsonData.ffcJSON_IsObject(root)) + { + error = "cJSON_Parse() failed"; + goto exit; + } + + cJSON* profiles = cjsonData.ffcJSON_GetObjectItemCaseSensitive(root, "profiles"); + if(!cjsonData.ffcJSON_IsObject(profiles)) + { + error = "cJSON_GetObjectItemCaseSensitive(root, \"profiles\") failed"; + goto exit; + } + + FFstrbuf wtProfileId; + ffStrbufInitS(&wtProfileId, getenv("WT_PROFILE_ID")); + ffStrbufTrim(&wtProfileId, '\''); + if(wtProfileId.length > 0) + { + cJSON* list = cjsonData.ffcJSON_GetObjectItemCaseSensitive(profiles, "list"); + if(cjsonData.ffcJSON_IsArray(list)) + { + cJSON* profile; + cJSON_ArrayForEach(profile, list) + { + if(!cjsonData.ffcJSON_IsObject(profile)) + continue; + cJSON* guid = cjsonData.ffcJSON_GetObjectItemCaseSensitive(profile, "guid"); + if(!cjsonData.ffcJSON_IsString(guid)) + continue; + if(ffStrbufCompS(&wtProfileId, cjsonData.ffcJSON_GetStringValue(guid)) == 0) + { + detectWTProfile(&cjsonData, profile, name, size); + break; + } + } + } + } + ffStrbufDestroy(&wtProfileId); + + cJSON* defaults = cjsonData.ffcJSON_GetObjectItemCaseSensitive(profiles, "defaults"); + detectWTProfile(&cjsonData, defaults, name, size); + + if(name->length == 0) + ffStrbufSetS(name, "Cascadia Mono"); + if(*size < 0) + *size = 12; + +exit: + cjsonData.ffcJSON_Delete(root); + dlclose(libcjson); + return error; +} + +#ifdef _WIN32 + #include "common/io.h" + + #include +#endif + +static void detectFromWindowsTeriminal(const FFinstance* instance, const FFstrbuf* terminalExe, FFTerminalFontResult* terminalFont) +{ + //https://learn.microsoft.com/en-us/windows/terminal/install#settings-json-file + FFstrbuf json; + ffStrbufInit(&json); + const char* error = NULL; + + #ifdef _WIN32 + if(terminalExe && terminalExe->length > 0 && !ffStrbufEqualS(terminalExe, "Windows Terminal")) + { + char jsonPath[MAX_PATH + 1]; + if(SUCCEEDED(SHGetFolderPathA(NULL, CSIDL_LOCAL_APPDATA, NULL, 0, jsonPath))) + { + size_t remaining = sizeof(jsonPath) - strlen(jsonPath) - 1; + if(ffStrbufContainIgnCaseS(terminalExe, "_8wekyb3d8bbwe\\")) + { + // Microsoft Store version + if(ffStrbufContainIgnCaseS(terminalExe, ".WindowsTerminalPreview_")) + { + // Preview version + strncat(jsonPath, "\\Packages\\Microsoft.WindowsTerminalPreview_8wekyb3d8bbwe\\LocalState\\settings.json", remaining); + if(!ffAppendFileBuffer(jsonPath, &json)) + error = "Error reading Windows Terminal Preview settings JSON file"; + } + else + { + // Stable version + strncat(jsonPath, "\\Packages\\Microsoft.WindowsTerminal_8wekyb3d8bbwe\\LocalState\\settings.json", remaining); + if(!ffAppendFileBuffer(jsonPath, &json)) + error = "Error reading Windows Terminal settings JSON file"; + } + } + else + { + strncat(jsonPath, "\\Microsoft\\Windows Terminal\\settings.json", remaining); + if(!ffAppendFileBuffer(jsonPath, &json)) + error = "Error reading Windows Terminal settings JSON file"; + } + } + } + #else + FF_UNUSED(terminalExe); + #endif + + if(!error && json.length == 0) + { + error = ffProcessAppendStdOut(&json, (char* const[]) { + "cmd.exe", + "/c", + //print the file content directly, so we don't need to handle the difference of Windows and POSIX path + "if exist %LOCALAPPDATA%\\Packages\\Microsoft.WindowsTerminal_8wekyb3d8bbwe\\LocalState\\settings.json " + "( type %LOCALAPPDATA%\\Packages\\Microsoft.WindowsTerminal_8wekyb3d8bbwe\\LocalState\\settings.json ) " + "else if exist %LOCALAPPDATA%\\Packages\\Microsoft.WindowsTerminalPreview_8wekyb3d8bbwe\\LocalState\\settings.json " + "( type %LOCALAPPDATA%\\Packages\\Microsoft.WindowsTerminalPreview_8wekyb3d8bbwe\\LocalState\\settings.json ) " + "else if exist \"%LOCALAPPDATA%\\Microsoft\\Windows Terminal\\settings.json\" " + "( type %LOCALAPPDATA%\\Microsoft\\Windows Terminal\\settings.json ) " + "else ( call )", + NULL + }); + } + + if(error) + { + ffStrbufAppendS(&terminalFont->error, error); + ffStrbufDestroy(&json); + return; + } + ffStrbufTrimRight(&json, '\n'); + if(json.length == 0) + { + ffStrbufAppendS(&terminalFont->error, "Cannot find file \"settings.json\""); + ffStrbufDestroy(&json); + return; + } + + FFstrbuf name; + ffStrbufInit(&name); + int size = -1; + error = detectFromWTImpl(instance, &json, &name, &size); + ffStrbufDestroy(&json); + + if(error) + ffStrbufAppendS(&terminalFont->error, error); + else + { + char sizeStr[16]; + snprintf(sizeStr, sizeof(sizeStr), "%d", size); + ffFontInitValues(&terminalFont->font, name.chars, sizeStr); + } + + ffStrbufDestroy(&name); +} + +#else + +static void detectFromWindowsTeriminal(const FFinstance* instance, const FFstrbuf* terminalExe, FFTerminalFontResult* terminalFont) +{ + FF_UNUSED(instance, terminalExe, terminalFont); + ffStrbufAppendS(&terminalFont->error, "fastfetch is built without libcjson support"); +} + +#endif +#endif //defined(_WIN32) || defined(__linux__) + +static bool detectKitty(const FFinstance* instance, FFTerminalFontResult* result) +{ + FFstrbuf fontName; + ffStrbufInit(&fontName); + + FFstrbuf fontSize; + ffStrbufInit(&fontSize); + + FFpropquery fontQuery[] = { + {"font_family ", &fontName}, + {"font_size ", &fontSize}, + }; + + if(!ffParsePropFileConfigValues(instance, "kitty/kitty.conf", 2, fontQuery)) + return false; + + if(fontName.length == 0) + ffStrbufSetS(&fontName, "monospace"); + if(fontSize.length == 0) + ffStrbufSetS(&fontSize, "11.0"); + + ffFontInitValues(&result->font, fontName.chars, fontSize.chars); + + ffStrbufDestroy(&fontName); + ffStrbufDestroy(&fontSize); + + return true; +} + void ffDetectTerminalFontPlatform(const FFinstance* instance, const FFTerminalShellResult* terminalShell, FFTerminalFontResult* terminalFont); +static bool detectTerminalFontCommon(const FFinstance* instance, const FFTerminalShellResult* terminalShell, FFTerminalFontResult* terminalFont) +{ + if(ffStrbufIgnCaseCompS(&terminalShell->terminalProcessName, "alacritty") == 0) + detectAlacritty(instance, terminalFont); + else if(ffStrbufIgnCaseCompS(&terminalShell->terminalProcessName, "kitty") == 0) + detectKitty(instance, terminalFont); + else if(ffStrbufStartsWithIgnCaseS(&terminalShell->terminalExe, "/dev/tty")) + detectTTY(terminalFont); + + #if defined(_WIN32) || defined(__linux__) + //Used by both Linux (WSL) and Windows + else if(ffStrbufIgnCaseCompS(&terminalShell->terminalProcessName, "Windows Terminal") == 0 || + ffStrbufIgnCaseCompS(&terminalShell->terminalProcessName, "WindowsTerminal.exe") == 0) + detectFromWindowsTeriminal(instance, &terminalShell->terminalExe, terminalFont); + #endif + + else + return false; + + return true; +} + const FFTerminalFontResult* ffDetectTerminalFont(const FFinstance* instance) { FF_DETECTION_INTERNAL_GUARD(FFTerminalFontResult, @@ -75,11 +353,8 @@ const FFTerminalFontResult* ffDetectTerminalFont(const FFinstance* instance) if(terminalShell->terminalProcessName.length == 0) ffStrbufAppendS(&result.error, "Terminal font needs successfull terminal detection"); - else if(ffStrbufIgnCaseCompS(&terminalShell->terminalProcessName, "alacritty") == 0) - detectAlacritty(instance, &result); - else if(ffStrbufStartsWithIgnCaseS(&terminalShell->terminalExe, "/dev/tty")) - detectTTY(&result); - else + + else if(!detectTerminalFontCommon(instance, terminalShell, &result)) ffDetectTerminalFontPlatform(instance, terminalShell, &result); if(result.error.length == 0 && result.font.pretty.length == 0) diff --git a/src/detection/terminalfont/terminalfont_android.c b/src/detection/terminalfont/terminalfont_android.c index 8211ab1d2..eaa4fa52d 100644 --- a/src/detection/terminalfont/terminalfont_android.c +++ b/src/detection/terminalfont/terminalfont_android.c @@ -1,10 +1,8 @@ #include "fastfetch.h" #include "terminalfont.h" -#include "detection/terminalshell.h" +#include "detection/terminalshell/terminalshell.h" #include "common/io.h" -#include - #ifdef FF_HAVE_FREETYPE #include "common/library.h" #include @@ -33,7 +31,7 @@ const char* detectTermux(const FFinstance* instance, FFTerminalFontResult* termi goto exit; } - if(ffFT_New_Face(library, FF_TERMUX_FONT_PATH, 0, &face )) + if(ffFT_New_Face(library, FF_TERMUX_FONT_PATH, 0, &face)) { error = "FT_NEW_Face(" FF_TERMUX_FONT_PATH ") failed"; goto exit; @@ -51,7 +49,7 @@ exit: #else FF_UNUSED(terminalFont); - ffStrbufSetS(&terminalFont->error, "fastfetch is built without freetype2 support"); + return "fastfetch is built without freetype2 support"; #endif } diff --git a/src/detection/terminalfont/terminalfont_apple.m b/src/detection/terminalfont/terminalfont_apple.m index ffe0064af..1ee387aa2 100644 --- a/src/detection/terminalfont/terminalfont_apple.m +++ b/src/detection/terminalfont/terminalfont_apple.m @@ -1,6 +1,6 @@ #include "terminalfont.h" #include "common/font.h" -#include "detection/terminalshell.h" +#include "detection/terminalshell/terminalshell.h" #include "util/apple/osascript.h" #include @@ -22,7 +22,7 @@ static void detectIterm2(const FFinstance* instance, FFTerminalFontResult* termi error:&error]; if(error) { - ffStrbufAppendS(&terminalFont->error, [error localizedDescription].UTF8String); + ffStrbufAppendS(&terminalFont->error, error.localizedDescription.UTF8String); return; } @@ -46,25 +46,42 @@ static void detectIterm2(const FFinstance* instance, FFTerminalFontResult* termi static void detectAppleTerminal(FFTerminalFontResult* terminalFont) { - FFstrbuf fontName; - ffStrbufInit(&fontName); - ffOsascript("tell application \"Terminal\" to font name of window frontmost", &fontName); + FF_STRBUF_AUTO_DESTROY font; + ffStrbufInit(&font); + ffOsascript("tell application \"Terminal\" to font name of window frontmost & \" \" & font size of window frontmost", &font); - if(fontName.length == 0) + if(font.length == 0) { ffStrbufAppendS(&terminalFont->error, "executing osascript failed"); - ffStrbufDestroy(&fontName); return; } - FFstrbuf fontSize; - ffStrbufInit(&fontSize); - ffOsascript("tell application \"Terminal\" to font size of window frontmost", &fontSize); + ffFontInitWithSpace(&terminalFont->font, font.chars); +} - ffFontInitValues(&terminalFont->font, fontName.chars, fontSize.chars); +static void detectWarpTerminal(const FFinstance* instance, FFTerminalFontResult* terminalFont) +{ + NSError* error; + NSString* fileName = [NSString stringWithFormat:@"file://%s/Library/Preferences/dev.warp.Warp-Stable.plist", instance->state.passwd->pw_dir]; + NSDictionary* dict = [NSDictionary dictionaryWithContentsOfURL:[NSURL URLWithString:fileName] + error:&error]; + if(error) + { + ffStrbufAppendS(&terminalFont->error, error.localizedDescription.UTF8String); + return; + } - ffStrbufDestroy(&fontName); - ffStrbufDestroy(&fontSize); + NSString* fontName = [dict valueForKey:@"FontName"]; + if(!fontName) + fontName = @"Hack"; + else + fontName = [fontName stringByTrimmingCharactersInSet:[NSCharacterSet characterSetWithCharactersInString:@"\""]]; + + NSString* fontSize = [dict valueForKey:@"FontSize"]; + if(!fontSize) + fontSize = @"13"; + + ffFontInitValues(&terminalFont->font, fontName.UTF8String, fontSize.UTF8String); } void ffDetectTerminalFontPlatform(const FFinstance* instance, const FFTerminalShellResult* terminalShell, FFTerminalFontResult* terminalFont) @@ -73,4 +90,6 @@ void ffDetectTerminalFontPlatform(const FFinstance* instance, const FFTerminalSh detectIterm2(instance, terminalFont); else if(ffStrbufIgnCaseCompS(&terminalShell->terminalProcessName, "Apple_Terminal") == 0) detectAppleTerminal(terminalFont); + else if(ffStrbufIgnCaseCompS(&terminalShell->terminalProcessName, "WarpTerminal") == 0) + detectWarpTerminal(instance, terminalFont); } diff --git a/src/detection/terminalfont/terminalfont_linux.c b/src/detection/terminalfont/terminalfont_linux.c index f68d45744..54ea3a328 100644 --- a/src/detection/terminalfont/terminalfont_linux.c +++ b/src/detection/terminalfont/terminalfont_linux.c @@ -2,7 +2,7 @@ #include "common/settings.h" #include "common/properties.h" #include "common/parsing.h" -#include "detection/terminalshell.h" +#include "detection/terminalshell/terminalshell.h" #include "detection/displayserver/displayserver.h" static const char* getSystemMonospaceFont(const FFinstance* instance) @@ -140,178 +140,56 @@ static void detectXFCETerminal(const FFinstance* instance, FFTerminalFontResult* ffStrbufDestroy(&useSysFont); } -#ifdef FF_HAVE_LIBCJSON - -#include "common/library.h" -#include "common/processing.h" - -#include -#include - -typedef struct CJSONData +static void detectDeepinTerminal(const FFinstance* instance, FFTerminalFontResult* terminalFont) { - FF_LIBRARY_SYMBOL(cJSON_Parse) - FF_LIBRARY_SYMBOL(cJSON_IsObject) - FF_LIBRARY_SYMBOL(cJSON_GetObjectItemCaseSensitive) - FF_LIBRARY_SYMBOL(cJSON_IsString) - FF_LIBRARY_SYMBOL(cJSON_GetStringValue) - FF_LIBRARY_SYMBOL(cJSON_IsNumber) - FF_LIBRARY_SYMBOL(cJSON_GetNumberValue) - FF_LIBRARY_SYMBOL(cJSON_IsArray) - FF_LIBRARY_SYMBOL(cJSON_Delete) -} CJSONData; + FFstrbuf fontName; + ffStrbufInit(&fontName); -static const char* detectWTProfile(CJSONData* cjsonData, cJSON* profile, FFstrbuf* name, int* size) -{ - if(!cjsonData->ffcJSON_IsObject(profile)) - return "cJSON_IsObject(profile) returns false"; + FFstrbuf fontSize; + ffStrbufInit(&fontSize); - cJSON* font = cjsonData->ffcJSON_GetObjectItemCaseSensitive(profile, "font"); - if(!cjsonData->ffcJSON_IsObject(font)) - return "cJSON_IsObject(font) returns false"; + FFstrbuf profile; + ffStrbufInitF(&profile, "%s/.config/deepin/deepin-terminal/config.conf", instance->state.passwd->pw_dir); + FILE* file = fopen(profile.chars, "r"); - if(name->length == 0) + if(file) { - cJSON* pface = cjsonData->ffcJSON_GetObjectItemCaseSensitive(font, "face"); - if(cjsonData->ffcJSON_IsString(pface)) - ffStrbufAppendS(name, cjsonData->ffcJSON_GetStringValue(pface)); - } - if(*size < 0) - { - cJSON* psize = cjsonData->ffcJSON_GetObjectItemCaseSensitive(font, "size"); - if(cjsonData->ffcJSON_IsNumber(psize)) - *size = (int)cjsonData->ffcJSON_GetNumberValue(psize); - } - return NULL; -} + char* line = NULL; + size_t len = 0; -static const char* detectFromWTImpl(const FFinstance* instance, FFstrbuf* content, FFstrbuf* name, int* size) -{ - CJSONData cjsonData; - - FF_LIBRARY_LOAD(libcjson, &instance->config.libcJSON, "dlopen libcjson failed", "libcjson"FF_LIBRARY_EXTENSION, 1) - FF_LIBRARY_LOAD_SYMBOL_VAR_MESSAGE(libcjson, cjsonData, cJSON_Parse) - FF_LIBRARY_LOAD_SYMBOL_VAR_MESSAGE(libcjson, cjsonData, cJSON_IsObject) - FF_LIBRARY_LOAD_SYMBOL_VAR_MESSAGE(libcjson, cjsonData, cJSON_GetObjectItemCaseSensitive) - FF_LIBRARY_LOAD_SYMBOL_VAR_MESSAGE(libcjson, cjsonData, cJSON_IsString) - FF_LIBRARY_LOAD_SYMBOL_VAR_MESSAGE(libcjson, cjsonData, cJSON_GetStringValue) - FF_LIBRARY_LOAD_SYMBOL_VAR_MESSAGE(libcjson, cjsonData, cJSON_IsNumber) - FF_LIBRARY_LOAD_SYMBOL_VAR_MESSAGE(libcjson, cjsonData, cJSON_GetNumberValue) - FF_LIBRARY_LOAD_SYMBOL_VAR_MESSAGE(libcjson, cjsonData, cJSON_IsArray) - FF_LIBRARY_LOAD_SYMBOL_VAR_MESSAGE(libcjson, cjsonData, cJSON_Delete) - - const char* error = NULL; - - cJSON* root = cjsonData.ffcJSON_Parse(content->chars); - if(!cjsonData.ffcJSON_IsObject(root)) - { - error = "cJSON_Parse() failed"; - goto exit; - } - - cJSON* profiles = cjsonData.ffcJSON_GetObjectItemCaseSensitive(root, "profiles"); - if(!cjsonData.ffcJSON_IsObject(profiles)) - { - error = "cJSON_GetObjectItemCaseSensitive(root, \"profiles\") failed"; - goto exit; - } - - FFstrbuf wtProfileId; - ffStrbufInitS(&wtProfileId, getenv("WT_PROFILE_ID")); - ffStrbufTrim(&wtProfileId, '\''); - if(wtProfileId.length > 0) - { - cJSON* list = cjsonData.ffcJSON_GetObjectItemCaseSensitive(profiles, "list"); - if(cjsonData.ffcJSON_IsArray(list)) + for(int count = 0; getline(&line, &len, file) != -1 && count < 2;) { - cJSON* profile; - cJSON_ArrayForEach(profile, list) + if(strcmp(line, "[basic.interface.font]\n") == 0) { - if(!cjsonData.ffcJSON_IsObject(profile)) - continue; - cJSON* guid = cjsonData.ffcJSON_GetObjectItemCaseSensitive(profile, "guid"); - if(!cjsonData.ffcJSON_IsString(guid)) - continue; - if(ffStrbufCompS(&wtProfileId, cjsonData.ffcJSON_GetStringValue(guid)) == 0) - { - detectWTProfile(&cjsonData, profile, name, size); - break; - } + if(getline(&line, &len, file) != -1) + ffParsePropLine(line, "value=", &fontName); + ++count; + } + else if(strcmp(line, "[basic.interface.font_size]\n") == 0) + { + if(getline(&line, &len, file) != -1) + ffParsePropLine(line, "value=", &fontSize); + ++count; } } - } - ffStrbufDestroy(&wtProfileId); - cJSON* defaults = cjsonData.ffcJSON_GetObjectItemCaseSensitive(profiles, "defaults"); - detectWTProfile(&cjsonData, defaults, name, size); - - if(name->length == 0) - ffStrbufSetS(name, "Cascadia Mono"); - if(*size < 0) - *size = 12; - -exit: - cjsonData.ffcJSON_Delete(root); - dlclose(libcjson); - return error; -} - -static void detectFromWindowsTeriminal(const FFinstance* instance, FFTerminalFontResult* terminalFont) -{ - //https://learn.microsoft.com/en-us/windows/terminal/install#settings-json-file - FFstrbuf json; - ffStrbufInit(&json); - const char* error; - error = ffProcessAppendStdOut(&json, (char* const[]) { - "cmd.exe", - "/c", - //print the file content directly, so we don't need to handle the difference of Windows and POSIX path - "if exist %LOCALAPPDATA%\\Packages\\Microsoft.WindowsTerminal_8wekyb3d8bbwe\\LocalState\\settings.json " - "( type %LOCALAPPDATA%\\Packages\\Microsoft.WindowsTerminal_8wekyb3d8bbwe\\LocalState\\settings.json ) " - "else if exist %LOCALAPPDATA%\\Packages\\Microsoft.WindowsTerminalPreview_8wekyb3d8bbwe\\LocalState\\settings.json " - "( type %LOCALAPPDATA%\\Packages\\Microsoft.WindowsTerminalPreview_8wekyb3d8bbwe\\LocalState\\settings.json ) " - "else if exist \"%LOCALAPPDATA%\\Microsoft\\Windows Terminal\\settings.json\" " - "( type %LOCALAPPDATA%\\Microsoft\\Windows Terminal\\settings.json ) " - "else ( call )", - NULL - }); - if(error) - { - ffStrbufAppendS(&terminalFont->error, error); - ffStrbufDestroy(&json); - return; - } - ffStrbufTrimRight(&json, '\n'); - if(json.length == 0) - { - ffStrbufAppendS(&terminalFont->error, "Cannot find file \"settings.json\""); - ffStrbufDestroy(&json); - return; + free(line); + fclose(file); } - FFstrbuf name; - ffStrbufInit(&name); - int size = -1; - detectFromWTImpl(instance, &json, &name, &size); - ffStrbufDestroy(&json); + ffStrbufDestroy(&profile); - char sizeStr[16]; - snprintf(sizeStr, sizeof(sizeStr), "%d", size); - ffFontInitValues(&terminalFont->font, name.chars, sizeStr); + if(fontName.length == 0) + ffStrbufAppendS(&fontName, "Noto Sans Mono"); + if(fontSize.length == 0) + ffStrbufAppendS(&fontSize, "11"); - ffStrbufDestroy(&name); + ffFontInitValues(&terminalFont->font, fontName.chars, fontSize.chars); + + ffStrbufDestroy(&fontName); + ffStrbufDestroy(&fontSize); } -#else - -static void detectFromWindowsTeriminal(const FFinstance* instance, FFTerminalFontResult* terminalFont) -{ - FF_UNUSED(instance, terminalFont); - ffStrbufAppendS(&terminalFont->error, "fastfetch is built without libcjson support"); -} - -#endif - void ffDetectTerminalFontPlatform(const FFinstance* instance, const FFTerminalShellResult* terminalShell, FFTerminalFontResult* terminalFont) { if(ffStrbufIgnCaseCompS(&terminalShell->terminalProcessName, "konsole") == 0) @@ -324,6 +202,6 @@ void ffDetectTerminalFontPlatform(const FFinstance* instance, const FFTerminalSh detectFromGSettings(instance, "/com/gexperts/Tilix/profiles/", "com.gexperts.Tilix.ProfilesList", "com.gexperts.Tilix.Profile", terminalFont); else if(ffStrbufIgnCaseCompS(&terminalShell->terminalProcessName, "gnome-terminal-") == 0) detectFromGSettings(instance, "/org/gnome/terminal/legacy/profiles:/:", "org.gnome.Terminal.ProfilesList", "org.gnome.Terminal.Legacy.Profile", terminalFont); - else if(ffStrbufIgnCaseCompS(&terminalShell->terminalProcessName, "Windows Terminal") == 0) - detectFromWindowsTeriminal(instance, terminalFont); + else if(ffStrbufIgnCaseCompS(&terminalShell->terminalProcessName, "deepin-terminal") == 0) + detectDeepinTerminal(instance, terminalFont); } diff --git a/src/detection/terminalfont/terminalfont_windows.c b/src/detection/terminalfont/terminalfont_windows.c new file mode 100644 index 000000000..ea41f018e --- /dev/null +++ b/src/detection/terminalfont/terminalfont_windows.c @@ -0,0 +1,108 @@ +#include "common/properties.h" +#include "common/io.h" +#include "detection/terminalshell/terminalshell.h" +#include "terminalfont.h" +#include "util/windows/unicode.h" + +#include + +static void detectMintty(const FFinstance* instance, FFTerminalFontResult* terminalFont) +{ + FF_STRBUF_AUTO_DESTROY fontName; + ffStrbufInit(&fontName); + + FF_STRBUF_AUTO_DESTROY fontSize; + ffStrbufInit(&fontSize); + + ffParsePropFileHomeValues(instance, ".minttyrc", 2, (FFpropquery[]) { + {"Font=", &fontName}, + {"FontHeight=", &fontSize} + }); + if(fontName.length == 0) + ffStrbufAppendS(&fontName, "Lucida Console"); + if(fontSize.length == 0) + ffStrbufAppendC(&fontSize, '9'); + + ffFontInitValues(&terminalFont->font, fontName.chars, fontSize.chars); +} + +static void detectConhost(const FFinstance* instance, FFTerminalFontResult* terminalFont) +{ + FF_UNUSED(instance); + + CONSOLE_FONT_INFOEX cfi = { .cbSize = sizeof(cfi) }; + if(!GetCurrentConsoleFontEx(GetStdHandle(STD_OUTPUT_HANDLE), FALSE, &cfi)) + { + ffStrbufAppendS(&terminalFont->error, "GetCurrentConsoleFontEx() failed"); + return; + } + + FF_STRBUF_AUTO_DESTROY fontName = ffStrbufCreateWS(cfi.FaceName); + + char fontSize[16]; + _ultoa((unsigned long)(cfi.dwFontSize.Y), fontSize, 10); + + ffFontInitValues(&terminalFont->font, fontName.chars, fontSize); +} + +static void detectConEmu(const FFinstance* instance, FFTerminalFontResult* terminalFont) +{ + FF_UNUSED(instance) + + //https://conemu.github.io/en/ConEmuXml.html#search-sequence + FFstrbuf path; + ffStrbufInit(&path); + + FFstrbuf fontName; + ffStrbufInit(&fontName); + + FFstrbuf fontSize; + ffStrbufInit(&fontSize); + + const char* paths[] = { "ConEmuDir", "ConEmuBaseDir", "APPDATA" }; + for (uint32_t i = 0; i < sizeof(paths) / sizeof(paths[0]); ++i) + { + ffStrbufSetS(&path, getenv(paths[i])); + if(path.length > 0) + { + ffStrbufAppendS(&path, "/ConEmu.xml"); + if(ffParsePropFileValues(path.chars, 2, (FFpropquery[]){ + {"error, "Failed to parse ConEmu.xml"); + return; + } + + if(fontName.length > 0) + ffStrbufSubstrBeforeLastC(&fontName, '"'); + else + ffStrbufAppendS(&fontName, "Consola"); + + if(fontSize.length > 0) + ffStrbufSubstrBeforeLastC(&fontSize, '"'); + else + ffStrbufAppendS(&fontSize, "14"); + + ffFontInitValues(&terminalFont->font, fontName.chars, fontSize.chars); + + ffStrbufDestroy(&fontName); + ffStrbufDestroy(&fontSize); +} + +void ffDetectTerminalFontPlatform(const FFinstance* instance, const FFTerminalShellResult* terminalShell, FFTerminalFontResult* terminalFont) +{ + if(ffStrbufIgnCaseCompS(&terminalShell->terminalProcessName, "mintty") == 0) + detectMintty(instance, terminalFont); + else if(ffStrbufIgnCaseCompS(&terminalShell->terminalProcessName, "conhost.exe") == 0) + detectConhost(instance, terminalFont); + else if(ffStrbufStartsWithIgnCaseS(&terminalShell->terminalProcessName, "ConEmuC")) + detectConEmu(instance, terminalFont); +} diff --git a/src/detection/terminalshell/terminalshell.c b/src/detection/terminalshell/terminalshell.c new file mode 100644 index 000000000..a99c498e4 --- /dev/null +++ b/src/detection/terminalshell/terminalshell.c @@ -0,0 +1,157 @@ +#include "fastfetch.h" +#include "common/processing.h" + +#ifdef _WIN32 + +#include + +static bool getFileVersion(const char* exePath, FFstrbuf* version) +{ + DWORD handle; + DWORD size = GetFileVersionInfoSizeA(exePath, &handle); + if(size > 0) + { + void* versionData = malloc(size); + if(GetFileVersionInfoA(exePath, handle, size, versionData)) + { + VS_FIXEDFILEINFO* verInfo; + UINT len; + if(VerQueryValueW(versionData, L"\\", (void**)&verInfo, &len) && len && verInfo->dwSignature == 0xFEEF04BD) + { + ffStrbufAppendF(version, "%u.%u.%u.%u", + (unsigned)(( verInfo->dwFileVersionMS >> 16 ) & 0xffff), + (unsigned)(( verInfo->dwFileVersionMS >> 0 ) & 0xffff), + (unsigned)(( verInfo->dwFileVersionLS >> 16 ) & 0xffff), + (unsigned)(( verInfo->dwFileVersionLS >> 0 ) & 0xffff) + ); + free(versionData); + return true; + } + } + free(versionData); + } + + return false; +} + +#endif + +static void getShellVersionBash(FFstrbuf* exe, FFstrbuf* version) +{ + ffProcessAppendStdOut(version, (char* const[]) { + exe->chars, + "--version", + NULL + }); // GNU bash, version 5.1.16(1)-release (x86_64-pc-msys)\nCopyright... + ffStrbufSubstrBeforeFirstC(version, '\n'); // GNU bash, version 5.1.16(1)-release (x86_64-pc-msys) + ffStrbufSubstrBeforeLastC(version, ' '); // GNU bash, version 5.1.16(1)-release + ffStrbufSubstrAfterLastC(version, ' '); // 5.1.16(1)-release + ffStrbufSubstrBeforeFirstC(version, '('); // 5.1.16 +} + +static void getShellVersionZsh(FFstrbuf* exe, FFstrbuf* version) +{ + ffProcessAppendStdOut(version, (char* const[]) { + exe->chars, + "--version", + NULL + }); + ffStrbufSubstrBeforeLastC(version, ' '); + ffStrbufSubstrAfterFirstC(version, ' '); +} + +static void getShellVersionFish(FFstrbuf* exe, FFstrbuf* version) +{ + ffProcessAppendStdOut(version, (char* const[]) { + exe->chars, + "--version", + NULL + }); + ffStrbufTrimRight(version, '\n'); + ffStrbufSubstrAfterLastC(version, ' '); +} + +static void getShellVersionTcsh(FFstrbuf* exe, FFstrbuf* version) +{ + ffProcessAppendStdOut(version, (char* const[]) { + exe->chars, + "--version", + NULL + }); // tcsh 6.24.01 (Astron) 2022-05-12 (aarch64-apple-darwin) options wide,nls,dl,al,kan,sm,rh,color,filec + ffStrbufSubstrAfterFirstC(version, ' '); // 6.24.01 (Astron) 2022-05-12 (aarch64-apple-darwin) options wide,nls,dl,al,kan,sm,rh,color,filec + ffStrbufSubstrBeforeFirstC(version, ' '); // 6.24.01 +} + +static void getShellVersionPwsh(FFstrbuf* exe, FFstrbuf* version) +{ + #ifdef _WIN32 + if(getFileVersion(exe->chars, version)) + return; + #endif + + ffProcessAppendStdOut(version, (char* const[]) { + exe->chars, + "--version", + NULL + }); + ffStrbufTrimRight(version, '\n'); + ffStrbufSubstrAfterLastC(version, ' '); +} + +#ifdef _WIN32 +static void getShellVersionWinPowerShell(FFstrbuf* exe, FFstrbuf* version) +{ + ffProcessAppendStdOut(version, (char* const[]) { + exe->chars, + "-NoLogo", + "-NoProfile", + "-Command", + "$PSVersionTable.PSVersion.ToString()", + NULL + }); + ffStrbufTrimRight(version, '\n'); + ffStrbufSubstrAfterLastC(version, ' '); +} + +static void getShellVersionCmd(FFstrbuf* exe, FFstrbuf* version) +{ + getFileVersion(exe->chars, version); +} +#endif + +static void getShellVersionNu(FFstrbuf* exe, FFstrbuf* version) +{ + ffProcessAppendStdOut(version, (char* const[]) { + exe->chars, + "--version", + NULL + }); +} + +bool fftsGetShellVersion(FFstrbuf* exe, const char* exeName, FFstrbuf* version) +{ + bool ok = true; + if(strcasecmp(exeName, "bash") == 0 || strcasecmp(exeName, "sh") == 0) + getShellVersionBash(exe, version); + else if(strcasecmp(exeName, "zsh") == 0) + getShellVersionZsh(exe, version); + else if(strcasecmp(exeName, "fish") == 0) + getShellVersionFish(exe, version); + else if(strcasecmp(exeName, "pwsh") == 0) + getShellVersionPwsh(exe, version); + else if(strcasecmp(exeName, "csh") == 0 || strcasecmp(exeName, "tcsh") == 0) + getShellVersionTcsh(exe, version); + else if(strcasecmp(exeName, "nu") == 0) + getShellVersionNu(exe, version); + + #ifdef _WIN32 + else if(strcasecmp(exeName, "powershell") == 0 || strcasecmp(exeName, "powershell_ise") == 0) + getShellVersionWinPowerShell(exe, version); + else if(strcasecmp(exeName, "cmd") == 0) + getShellVersionCmd(exe, version); + #endif + + else + ok = false; + return ok; +} diff --git a/src/detection/terminalshell.h b/src/detection/terminalshell/terminalshell.h similarity index 91% rename from src/detection/terminalshell.h rename to src/detection/terminalshell/terminalshell.h index 2791856ac..8783e3410 100644 --- a/src/detection/terminalshell.h +++ b/src/detection/terminalshell/terminalshell.h @@ -10,10 +10,12 @@ typedef struct FFTerminalShellResult FFstrbuf shellProcessName; FFstrbuf shellExe; const char* shellExeName; //pointer to a char in shellExe + FFstrbuf shellPrettyName; FFstrbuf shellVersion; FFstrbuf terminalProcessName; FFstrbuf terminalExe; + FFstrbuf terminalPrettyName; const char* terminalExeName; //pointer to a char in terminalExe FFstrbuf userShellExe; diff --git a/src/detection/terminalshell/terminalshell_linux.c b/src/detection/terminalshell/terminalshell_linux.c new file mode 100644 index 000000000..80c0d93d1 --- /dev/null +++ b/src/detection/terminalshell/terminalshell_linux.c @@ -0,0 +1,383 @@ +#include "fastfetch.h" +#include "common/io.h" +#include "common/parsing.h" +#include "common/processing.h" +#include "common/thread.h" +#include "terminalshell.h" + +#include +#include +#include +#include + +#ifdef __APPLE__ + #include +#elif defined(__FreeBSD__) + #include + #include + #include +#endif + +static void setExeName(FFstrbuf* exe, const char** exeName) +{ + assert(exe->length > 0); + uint32_t lastSlashIndex = ffStrbufLastIndexC(exe, '/'); + if(lastSlashIndex < exe->length) + *exeName = exe->chars + lastSlashIndex + 1; +} + +static void getProcessInformation(pid_t pid, FFstrbuf* processName, FFstrbuf* exe, const char** exeName) +{ + assert(processName->length > 0); + ffStrbufClear(exe); + + #ifdef __linux__ + + char cmdlineFilePath[64]; + snprintf(cmdlineFilePath, sizeof(cmdlineFilePath), "/proc/%d/cmdline", (int)pid); + + if(ffAppendFileBuffer(cmdlineFilePath, exe)) + { + ffStrbufSubstrBeforeFirstC(exe, '\0'); //Trim the arguments + ffStrbufTrimLeft(exe, '-'); //Happens in TTY + } + + #elif defined(__APPLE__) + + int length = proc_pidpath((int)pid, exe->chars, exe->allocated); + if(length > 0) + exe->length = (uint32_t)length; + + #else + + size_t size = exe->allocated; + if(!sysctl( + (int[]){CTL_KERN, KERN_PROC, KERN_PROC_PATHNAME, pid}, 4, + exe->chars, &size, + NULL, 0 + )) + exe->length = (uint32_t)size; + + #endif + + if(exe->length == 0) + ffStrbufSet(exe, processName); + + setExeName(exe, exeName); +} + +static const char* getProcessNameAndPpid(pid_t pid, char* name, pid_t* ppid) +{ + const char* error = NULL; + + #ifdef __linux__ + + char statFilePath[64]; + snprintf(statFilePath, sizeof(statFilePath), "/proc/%d/stat", (int)pid); + FILE* stat = fopen(statFilePath, "r"); + if(stat == NULL) + return "fopen(statFilePath, \"r\") failed"; + + *ppid = 0; + if( + fscanf(stat, "%*s (%255[^)]) %*c %d", name, ppid) != 2 || //stat (comm) state ppid + !ffStrSet(name) || + *ppid == 0 + ) + error = "fscanf(stat) failed"; + + fclose(stat); + + #elif defined(__APPLE__) + + struct proc_bsdshortinfo proc; + if(proc_pidinfo(pid, PROC_PIDT_SHORTBSDINFO, 0, &proc, PROC_PIDT_SHORTBSDINFO_SIZE) <= 0) + error = "proc_pidinfo(pid) failed"; + else + { + *ppid = (pid_t)proc.pbsi_ppid; + strncpy(name, proc.pbsi_comm, 16); //trancated to 16 chars + } + + #else + + struct kinfo_proc proc; + size_t size = sizeof(proc); + if(sysctl( + (int[]){CTL_KERN, KERN_PROC, KERN_PROC_PID, pid}, 4, + &proc, &size, + NULL, 0 + )) + error = "sysctl(KERN_PROC_PID) failed"; + else + { + *ppid = (pid_t)proc.ki_ppid; + strncpy(name, proc.ki_comm, COMMLEN); + } + + #endif + + return error; +} + +static void getTerminalShell(FFTerminalShellResult* result, pid_t pid) +{ + char name[256]; + name[0] = '\0'; + + pid_t ppid = 0; + + if(getProcessNameAndPpid(pid, name, &ppid)) + return; + + //Common programs that are between terminal and own process, but are not the shell + if( + strcasecmp(name, "sh") == 0 || //This prevents us from detecting things like pipes and redirects, i hope nobody uses plain `sh` as shell + strcasecmp(name, "sudo") == 0 || + strcasecmp(name, "su") == 0 || + strcasecmp(name, "doas") == 0 || + strcasecmp(name, "strace") == 0 || + strcasecmp(name, "sshd") == 0 || + strcasecmp(name, "gdb") == 0 || + strcasecmp(name, "lldb") == 0 || + strcasecmp(name, "guake-wrapped") == 0 || + strcasestr(name, "debug") != NULL + ) { + getTerminalShell(result, ppid); + return; + } + + //Known shells + if( + strcasecmp(name, "bash") == 0 || + strcasecmp(name, "zsh") == 0 || + strcasecmp(name, "ksh") == 0 || + strcasecmp(name, "csh") == 0 || + strcasecmp(name, "tcsh") == 0 || + strcasecmp(name, "fish") == 0 || + strcasecmp(name, "dash") == 0 || + strcasecmp(name, "pwsh") == 0 || + strcasecmp(name, "nu") == 0 || + strcasecmp(name, "git-shell") == 0 + ) { + if (result->shellProcessName.length == 0) + { + ffStrbufSetS(&result->shellProcessName, name); + getProcessInformation(pid, &result->shellProcessName, &result->shellExe, &result->shellExeName); + } + + getTerminalShell(result, ppid); + return; + } + + ffStrbufSetS(&result->terminalProcessName, name); + getProcessInformation(pid, &result->terminalProcessName, &result->terminalExe, &result->terminalExeName); +} + +static void getTerminalFromEnv(FFTerminalShellResult* result) +{ + if( + result->terminalProcessName.length > 0 && + !ffStrbufStartsWithIgnCaseS(&result->terminalProcessName, "login") && + !ffStrbufIgnCaseEqualS(&result->terminalProcessName, "(login)") && + + #ifdef __APPLE__ + !ffStrbufIgnCaseEqualS(&result->terminalProcessName, "launchd") && + !ffStrbufIgnCaseEqualS(&result->terminalProcessName, "stable") && //for WarpTerminal + #else + !ffStrbufIgnCaseEqualS(&result->terminalProcessName, "systemd") && + !ffStrbufIgnCaseEqualS(&result->terminalProcessName, "init") && + !ffStrbufIgnCaseEqualS(&result->terminalProcessName, "(init)") && + #endif + + ffStrbufIgnCaseCompS(&result->terminalProcessName, "0") != 0 + ) return; + + const char* term = NULL; + + //SSH + if(getenv("SSH_CONNECTION") != NULL) + term = getenv("SSH_TTY"); + + #ifdef __linux__ + //Windows Terminal + if(!ffStrSet(term) && ( + getenv("WT_SESSION") != NULL || + getenv("WT_PROFILE_ID") != NULL + )) term = "Windows Terminal"; + #endif + + //Alacritty + if(!ffStrSet(term) && ( + getenv("ALACRITTY_SOCKET") != NULL || + getenv("ALACRITTY_LOG") != NULL || + getenv("ALACRITTY_WINDOW_ID") != NULL + )) term = "Alacritty"; + + #ifdef __ANDROID__ + //Termux + if(!ffStrSet(term) && ( + getenv("TERMUX_VERSION") != NULL || + getenv("TERMUX_MAIN_PACKAGE_FORMAT") != NULL || + getenv("TMUX_TMPDIR") != NULL + )) term = "Termux"; + #endif + + #ifdef __linux__ + //Konsole + if(!ffStrSet(term) && ( + getenv("KONSOLE_VERSION") != NULL + )) term = "konsole"; + #endif + + //MacOS, mintty + if(!ffStrSet(term)) + term = getenv("TERM_PROGRAM"); + + #ifdef __linux__ + if(!ffStrSet(term)) + { + //We are in WSL but not in Windows Terminal + if(getenv("WSL_DISTRO") != NULL || getenv("WSL_INTEROP") != NULL) + term = "conhost"; + } + #endif + + //Normal Terminal + if(!ffStrSet(term)) + term = getenv("TERM"); + + //TTY + if(!ffStrSet(term) || strcasecmp(term, "linux") == 0) + term = ttyname(STDIN_FILENO); + + if(ffStrSet(term)) + { + ffStrbufSetS(&result->terminalProcessName, term); + ffStrbufSetS(&result->terminalExe, term); + setExeName(&result->terminalExe, &result->terminalExeName); + } +} + +static void getUserShellFromEnv(const FFinstance* instance, FFTerminalShellResult* result) +{ + if(instance->state.passwd->pw_shell[0] != '\0') + ffStrbufAppendS(&result->userShellExe, instance->state.passwd->pw_shell); + else + ffStrbufAppendS(&result->userShellExe, getenv("SHELL")); + if(result->userShellExe.length == 0) + return; + + setExeName(&result->userShellExe, &result->userShellExeName); + + //If shell detection via processes failed + if(result->shellProcessName.length == 0 && result->userShellExe.length > 0) + { + ffStrbufAppendS(&result->shellProcessName, result->userShellExeName); + ffStrbufSet(&result->shellExe, &result->userShellExe); + setExeName(&result->shellExe, &result->shellExeName); + } +} + +static void getShellVersionGeneric(FFstrbuf* exe, const char* exeName, FFstrbuf* version) +{ + FFstrbuf command; + ffStrbufInit(&command); + ffStrbufAppendS(&command, "printf \"%s\" \"$"); + ffStrbufAppendTransformS(&command, exeName, toupper); + ffStrbufAppendS(&command, "_VERSION\""); + + ffProcessAppendStdOut(version, (char* const[]) { + "env", + "-i", + exe->chars, + "-c", + command.chars, + NULL + }); + ffStrbufSubstrBeforeFirstC(version, '('); + ffStrbufRemoveStrings(version, 2, "-release", "release"); + + ffStrbufDestroy(&command); +} + +bool fftsGetShellVersion(FFstrbuf* exe, const char* exeName, FFstrbuf* version); + +static void getShellVersion(FFstrbuf* exe, const char* exeName, FFstrbuf* version) +{ + ffStrbufClear(version); + if(!fftsGetShellVersion(exe, exeName, version)) + getShellVersionGeneric(exe, exeName, version); +} + +const FFTerminalShellResult* ffDetectTerminalShell(const FFinstance* instance) +{ + FF_UNUSED(instance); + + static FFThreadMutex mutex = FF_THREAD_MUTEX_INITIALIZER; + static FFTerminalShellResult result; + static bool init = false; + ffThreadMutexLock(&mutex); + if(init) + { + ffThreadMutexUnlock(&mutex); + return &result; + } + init = true; + + #ifdef __APPLE__ + const uint32_t exePathLen = PROC_PIDPATHINFO_MAXSIZE; + #elif defined(MAXPATH) + const uint32_t exePathLen = MAXPATH; + #else + const uint32_t exePathLen = 260; + #endif + + ffStrbufInit(&result.shellProcessName); + ffStrbufInitA(&result.shellExe, exePathLen); + result.shellExeName = result.shellExe.chars; + ffStrbufInit(&result.shellVersion); + + ffStrbufInit(&result.terminalProcessName); + ffStrbufInitA(&result.terminalExe, exePathLen); + result.terminalExeName = result.terminalExe.chars; + + ffStrbufInit(&result.userShellExe); + result.userShellExeName = result.userShellExe.chars; + ffStrbufInit(&result.userShellVersion); + + getTerminalShell(&result, getppid()); + + getTerminalFromEnv(&result); + getUserShellFromEnv(instance, &result); + getShellVersion(&result.shellExe, result.shellExeName, &result.shellVersion); + + if(strcasecmp(result.shellExeName, result.userShellExeName) != 0) + getShellVersion(&result.userShellExe, result.userShellExeName, &result.userShellVersion); + else + ffStrbufSet(&result.userShellVersion, &result.shellVersion); + + if(ffStrbufEqualS(&result.shellProcessName, "pwsh")) + ffStrbufInitS(&result.shellPrettyName, "PowerShell"); + else if(ffStrbufEqualS(&result.shellProcessName, "nu")) + ffStrbufInitS(&result.shellPrettyName, "nushell"); + else + { + // https://github.com/LinusDierheimer/fastfetch/discussions/280#discussioncomment-3831734 + ffStrbufInitS(&result.shellPrettyName, result.shellExeName); + } + + if(ffStrbufEqualS(&result.terminalProcessName, "iTerm.app")) + ffStrbufInitS(&result.terminalPrettyName, "iTerm"); + else if(ffStrbufEqualS(&result.terminalProcessName, "Apple_Terminal")) + ffStrbufInitS(&result.terminalPrettyName, "Apple Terminal"); + else if(ffStrbufEqualS(&result.terminalProcessName, "WarpTerminal")) + ffStrbufInitS(&result.terminalPrettyName, "Warp"); + else if(strncmp(result.terminalExeName, result.terminalProcessName.chars, result.terminalProcessName.length) == 0) // if exeName starts with processName, print it. Otherwise print processName + ffStrbufInitS(&result.terminalPrettyName, result.terminalExeName); + else + ffStrbufInitCopy(&result.terminalPrettyName, &result.terminalProcessName); + + ffThreadMutexUnlock(&mutex); + return &result; +} diff --git a/src/detection/terminalshell/terminalshell_windows.cpp b/src/detection/terminalshell/terminalshell_windows.cpp new file mode 100644 index 000000000..abc22a007 --- /dev/null +++ b/src/detection/terminalshell/terminalshell_windows.cpp @@ -0,0 +1,306 @@ +extern "C" { +#include "terminalshell.h" +#include "common/processing.h" +#include "common/thread.h" +} + +#include +#include +#include + +#ifdef FF_USE_WIN_NTAPI + +#include + +static bool getProcessInfo(uint32_t pid, uint32_t* ppid, FFstrbuf* pname, FFstrbuf* exe, const char** exeName) +{ + HANDLE hProcess = pid == 0 + ? GetCurrentProcess() + : OpenProcess(PROCESS_QUERY_LIMITED_INFORMATION, TRUE, pid); + + if(ppid) + { + PROCESS_BASIC_INFORMATION info; + ULONG size; + if(NT_SUCCESS(NtQueryInformationProcess(hProcess, ProcessBasicInformation, &info, sizeof(info), &size))) + { + assert(size == sizeof(info)); + *ppid = (uint32_t)info.InheritedFromUniqueProcessId; + } + else + { + CloseHandle(hProcess); + return false; + } + } + if(exe) + { + DWORD bufSize = exe->allocated; + if(QueryFullProcessImageNameA(hProcess, 0, exe->chars, &bufSize)) + exe->length = bufSize; + else + { + CloseHandle(hProcess); + return false; + } + } + if(pname && exeName) + { + *exeName = exe->chars + ffStrbufLastIndexC(exe, '\\') + 1; + ffStrbufSetS(pname, *exeName); + } + + CloseHandle(hProcess); + return true; +} + +#else + +#include "util/windows/wmi.hpp" +#include + +static bool getProcessInfo(uint32_t pid, uint32_t* ppid, FFstrbuf* pname, FFstrbuf* exe, const char** exeName) +{ + if(pid == 0) + pid = GetCurrentProcessId(); + + wchar_t sql[256] = {}; + swprintf(sql, 256, L"SELECT %ls %ls ParentProcessId FROM Win32_Process WHERE ProcessId = %" PRIu32, + pname ? L"Name," : L"", + pname ? L"ExecutablePath," : L"", + pid); + + FFWmiQuery query(sql); + if(!query) + return false; + + if(FFWmiRecord record = query.next()) + { + if(ppid) + { + uint64_t value; + record.getUnsigned(L"ParentProcessId", &value); + *ppid = (uint32_t) value; + } + + if(pname) + record.getString(L"Name", pname); + + if(exe) + record.getString(L"ExecutablePath", exe); + + if(exeName) + *exeName = exe->chars + ffStrbufLastIndexC(exe, '\\') + 1; + } + return true; +} + +#endif + +extern "C" bool fftsGetShellVersion(FFstrbuf* exe, const char* exeName, FFstrbuf* version); + +static uint32_t getShellInfo(FFTerminalShellResult* result, uint32_t pid) +{ + uint32_t ppid; + + if(pid == 0 || !getProcessInfo(pid, &ppid, &result->shellProcessName, &result->shellExe, &result->shellExeName)) + return 0; + + ffStrbufSet(&result->shellPrettyName, &result->shellProcessName); + if(ffStrbufEndsWithIgnCaseS(&result->shellPrettyName, ".exe")) + ffStrbufSubstrBefore(&result->shellPrettyName, result->shellPrettyName.length - 4); + + //Common programs that are between terminal and own process, but are not the shell + if( + ffStrbufIgnCaseEqualS(&result->shellPrettyName, "sudo") || + ffStrbufIgnCaseEqualS(&result->shellPrettyName, "su") || + ffStrbufIgnCaseEqualS(&result->shellPrettyName, "doas") || + ffStrbufIgnCaseEqualS(&result->shellPrettyName, "strace") || + ffStrbufIgnCaseEqualS(&result->shellPrettyName, "sshd") || + ffStrbufIgnCaseEqualS(&result->shellPrettyName, "gdb") || + ffStrbufIgnCaseEqualS(&result->shellPrettyName, "lldb") || + ffStrbufIgnCaseEqualS(&result->shellPrettyName, "guake-wrapped") || + ffStrbufContainIgnCaseS(&result->shellPrettyName, "debug") + ) { + ffStrbufClear(&result->shellProcessName); + ffStrbufClear(&result->shellPrettyName); + ffStrbufClear(&result->shellExe); + result->shellExeName = nullptr; + return getShellInfo(result, ppid); + } + + ffStrbufClear(&result->shellVersion); + fftsGetShellVersion(&result->shellExe, result->shellPrettyName.chars, &result->shellVersion); + + if(ffStrbufIgnCaseEqualS(&result->shellPrettyName, "pwsh")) + ffStrbufSetS(&result->shellPrettyName, "PowerShell"); + else if(ffStrbufIgnCaseEqualS(&result->shellPrettyName, "powershell")) + ffStrbufSetS(&result->shellPrettyName, "Windows PowerShell"); + else if(ffStrbufIgnCaseEqualS(&result->shellPrettyName, "powershell_ise")) + ffStrbufSetS(&result->shellPrettyName, "Windows PowerShell ISE"); + else if(ffStrbufIgnCaseEqualS(&result->shellPrettyName, "cmd")) + { + ffStrbufClear(&result->shellPrettyName); + + HANDLE snapshot; + while(!(snapshot = CreateToolhelp32Snapshot(TH32CS_SNAPMODULE, pid)) && GetLastError() == ERROR_BAD_LENGTH) {} + + if(snapshot) + { + MODULEENTRY32W module; + module.dwSize = sizeof(module); + for(BOOL success = Module32FirstW(snapshot, &module); success; success = Module32NextW(snapshot, &module)) + { + if(wcsncmp(module.szModule, L"clink_dll_", wcslen(L"clink_dll_")) == 0) + { + ffStrbufAppendS(&result->shellPrettyName, "CMD (with Clink)"); + break; + } + } + CloseHandle(snapshot); + } + if(result->shellPrettyName.length == 0) + ffStrbufAppendS(&result->shellPrettyName, "Command Prompt"); + } + else if(ffStrbufIgnCaseEqualS(&result->shellPrettyName, "nu")) + ffStrbufSetS(&result->shellPrettyName, "nushell"); + else if(ffStrbufIgnCaseEqualS(&result->shellPrettyName, "explorer")) + { + ffStrbufSetS(&result->shellPrettyName, "Windows Explorer"); // Started without shell + return 0; + } + + return ppid; +} + +static uint32_t getTerminalInfo(FFTerminalShellResult* result, uint32_t pid) +{ + uint32_t ppid; + + if(pid == 0 || !getProcessInfo(pid, &ppid, &result->terminalProcessName, &result->terminalExe, &result->terminalExeName)) + return 0; + + ffStrbufSet(&result->terminalPrettyName, &result->terminalProcessName); + if(ffStrbufEndsWithIgnCaseS(&result->terminalPrettyName, ".exe")) + ffStrbufSubstrBefore(&result->terminalPrettyName, result->terminalPrettyName.length - 4); + + if( + ffStrbufIgnCaseEqualS(&result->terminalPrettyName, "pwsh") || + ffStrbufIgnCaseEqualS(&result->terminalPrettyName, "cmd") || + ffStrbufIgnCaseEqualS(&result->terminalPrettyName, "bash") || + ffStrbufIgnCaseEqualS(&result->terminalPrettyName, "zsh") || + ffStrbufIgnCaseEqualS(&result->terminalPrettyName, "fish") || + ffStrbufIgnCaseEqualS(&result->terminalPrettyName, "nu") || + ffStrbufIgnCaseEqualS(&result->terminalPrettyName, "powershell") || + ffStrbufIgnCaseEqualS(&result->terminalPrettyName, "powershell_ise") + ) { + //We are nested shell + ffStrbufClear(&result->terminalProcessName); + ffStrbufClear(&result->terminalPrettyName); + ffStrbufClear(&result->terminalExe); + result->terminalExeName = ""; + return getTerminalInfo(result, ppid); + } + + if(ffStrbufIgnCaseEqualS(&result->terminalPrettyName, "WindowsTerminal")) + ffStrbufSetS(&result->terminalPrettyName, ffStrbufContainIgnCaseS(&result->terminalExe, ".WindowsTerminalPreview_") + ? "Windows Terminal Preview" + : "Windows Terminal" + ); + else if(ffStrbufIgnCaseEqualS(&result->terminalPrettyName, "conhost")) + ffStrbufSetS(&result->terminalPrettyName, "Console Window Host"); + else if(ffStrbufIgnCaseEqualS(&result->terminalPrettyName, "Code")) + ffStrbufSetS(&result->terminalPrettyName, "Visual Studio Code"); + else if(ffStrbufIgnCaseEqualS(&result->terminalPrettyName, "explorer")) + ffStrbufSetS(&result->terminalPrettyName, "Windows Explorer"); + else if(ffStrbufStartsWithIgnCaseS(&result->terminalPrettyName, "ConEmuC")) + ffStrbufSetS(&result->terminalPrettyName, "ConEmu"); + + return ppid; +} + +static void getTerminalFromEnv(FFTerminalShellResult* result) +{ + if( + result->terminalProcessName.length > 0 && + ffStrbufIgnCaseCompS(&result->terminalProcessName, "explorer") != 0 + ) return; + + const char* term = nullptr; + + //SSH + if(getenv("SSH_CONNECTION") != nullptr) + term = getenv("SSH_TTY"); + + //Windows Terminal + if(!term && ( + getenv("WT_SESSION") != nullptr || + getenv("WT_PROFILE_ID") != nullptr + )) term = "Windows Terminal"; + + //Alacritty + if(!term && ( + getenv("ALACRITTY_SOCKET") != nullptr || + getenv("ALACRITTY_LOG") != nullptr || + getenv("ALACRITTY_WINDOW_ID") != nullptr + )) term = "Alacritty"; + + if(!term) + term = getenv("TERM_PROGRAM"); + + //Normal Terminal + if(!term) + term = getenv("TERM"); + + if(term) + { + ffStrbufSetS(&result->terminalProcessName, term); + ffStrbufSetS(&result->terminalPrettyName, term); + ffStrbufSetS(&result->terminalExe, term); + result->terminalExeName = ""; + } +} + +const FFTerminalShellResult* ffDetectTerminalShell(const FFinstance* instance) +{ + FF_UNUSED(instance); + + static FFThreadMutex mutex = FF_THREAD_MUTEX_INITIALIZER; + static FFTerminalShellResult result; + static bool init = false; + ffThreadMutexLock(&mutex); + if(init) + { + ffThreadMutexUnlock(&mutex); + return &result; + } + init = true; + + ffStrbufInit(&result.shellProcessName); + ffStrbufInitA(&result.shellExe, 128); + result.shellExeName = ""; + ffStrbufInit(&result.shellPrettyName); + ffStrbufInit(&result.shellVersion); + + ffStrbufInit(&result.terminalProcessName); + ffStrbufInitA(&result.terminalExe, 128); + result.terminalExeName = ""; + ffStrbufInit(&result.terminalPrettyName); + + ffStrbufInit(&result.userShellExe); + result.userShellExeName = ""; + ffStrbufInit(&result.userShellVersion); + + uint32_t ppid; + if(!getProcessInfo(0, &ppid, nullptr, nullptr, nullptr)) + goto exit; + + ppid = getShellInfo(&result, ppid); + getTerminalInfo(&result, ppid); + if(result.terminalProcessName.length == 0) + getTerminalFromEnv(&result); + +exit: + ffThreadMutexUnlock(&mutex); + return &result; +} diff --git a/src/detection/title.c b/src/detection/title.c index b4394be15..df9367ba0 100644 --- a/src/detection/title.c +++ b/src/detection/title.c @@ -1,16 +1,17 @@ #include "fastfetch.h" #include "detection/title.h" +#include "common/thread.h" #include #include -#include -#include #ifndef HOST_NAME_MAX #define HOST_NAME_MAX 64 #endif #ifdef __linux__ +#include + static void detectFQDN(FFTitleResult* title) { struct addrinfo hints = {0}; @@ -37,12 +38,12 @@ const FFTitleResult* ffDetectTitle(const FFinstance* instance) { static FFTitleResult 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; @@ -54,12 +55,12 @@ const FFTitleResult* ffDetectTitle(const FFinstance* instance) ffStrbufAppendS(&result.hostname, instance->state.utsname.nodename); ffStrbufInitA(&result.fqdn, HOST_NAME_MAX); - #ifdef __linux + #ifdef __linux__ detectFQDN(&result); #endif if(result.fqdn.length == 0) ffStrbufAppend(&result.fqdn, &result.hostname); - pthread_mutex_unlock(&mutex); + ffThreadMutexUnlock(&mutex); return &result; } diff --git a/src/detection/uptime/uptime.h b/src/detection/uptime/uptime.h new file mode 100644 index 000000000..6a032b47c --- /dev/null +++ b/src/detection/uptime/uptime.h @@ -0,0 +1,10 @@ +#pragma once + +#ifndef FF_INCLUDED_detection_uptime_uptime +#define FF_INCLUDED_detection_uptime_uptime + +#include "fastfetch.h" + +uint64_t ffDetectUptime(); + +#endif diff --git a/src/detection/uptime/uptime_bsd.c b/src/detection/uptime/uptime_bsd.c new file mode 100644 index 000000000..375065c35 --- /dev/null +++ b/src/detection/uptime/uptime_bsd.c @@ -0,0 +1,19 @@ +#include "uptime.h" + +#include +#include +#include + +uint64_t ffDetectUptime() +{ + struct timeval bootTime; + size_t bootTimeSize = sizeof(bootTime); + if(sysctl( + (int[]) {CTL_KERN, KERN_BOOTTIME}, 2, + &bootTime, &bootTimeSize, + NULL, 0 + ) == 0) + return (uint64_t) difftime(time(NULL), bootTime.tv_sec); + + return 0; +} diff --git a/src/detection/uptime/uptime_linux.c b/src/detection/uptime/uptime_linux.c new file mode 100644 index 000000000..cf4860771 --- /dev/null +++ b/src/detection/uptime/uptime_linux.c @@ -0,0 +1,11 @@ +#include "uptime.h" + +#include + +uint64_t ffDetectUptime() +{ + struct sysinfo info; + if(sysinfo(&info) != 0) + return 0; + return (uint32_t) info.uptime; +} diff --git a/src/detection/uptime/uptime_windows.c b/src/detection/uptime/uptime_windows.c new file mode 100644 index 000000000..9f2dab02a --- /dev/null +++ b/src/detection/uptime/uptime_windows.c @@ -0,0 +1,9 @@ +#include "uptime.h" + +#define WIN32_LEAN_AND_MEAN +#include + +uint64_t ffDetectUptime() +{ + return GetTickCount64() / 1000; +} diff --git a/src/detection/users/users.h b/src/detection/users/users.h new file mode 100644 index 000000000..8842e16a2 --- /dev/null +++ b/src/detection/users/users.h @@ -0,0 +1,10 @@ +#pragma once + +#ifndef FF_INCLUDED_detection_users_users +#define FF_INCLUDED_detection_users_users + +#include "fastfetch.h" + +void ffDetectUsers(FFlist* users /* List of FFstrbuf */, FFstrbuf* error); + +#endif diff --git a/src/detection/users/users_linux.c b/src/detection/users/users_linux.c new file mode 100644 index 000000000..b1dcee8ed --- /dev/null +++ b/src/detection/users/users_linux.c @@ -0,0 +1,36 @@ +#include "fastfetch.h" +#include "users.h" + +#if FF_HAVE_UTMPX_H + #include +#else + //for Android compatibility + #include + #define utmpx utmp + #define setutxent setutent + #define getutxent getutent +#endif + +void ffDetectUsers(FFlist* users, FFstrbuf* error) +{ + struct utmpx* n = NULL; + setutxent(); + +next: + while((n = getutxent())) + { + if(n->ut_type != USER_PROCESS) + continue; + + for(uint32_t i = 0; i < users->length; ++i) + { + if(ffStrbufCompS((FFstrbuf*)ffListGet(users, i), n->ut_user) == 0) + goto next; + } + + ffStrbufInitS((FFstrbuf*)ffListAdd(users), n->ut_user); + } + + if(users->length == 0) + ffStrbufAppendS(error, "Unable to detect users"); +} diff --git a/src/detection/users/users_windows.c b/src/detection/users/users_windows.c new file mode 100644 index 000000000..0b4316c10 --- /dev/null +++ b/src/detection/users/users_windows.c @@ -0,0 +1,55 @@ +#include "users.h" +#include "util/windows/unicode.h" + +#include + +//at the time of writing, of MinGW doesn't have the definition of WTSEnumerateSessionsExW +typedef struct _WTS_SESSION_INFO_1W { + DWORD ExecEnvId; + WTS_CONNECTSTATE_CLASS State; + DWORD SessionId; + LPWSTR pSessionName; + LPWSTR pHostName; + LPWSTR pUserName; + LPWSTR pDomainName; + LPWSTR pFarmName; +} WTS_SESSION_INFO_1W, * PWTS_SESSION_INFO_1W; + +BOOL +WINAPI +WTSEnumerateSessionsExW( + HANDLE hServer, + DWORD* pLevel, + DWORD Filter, + PWTS_SESSION_INFO_1W* ppSessionInfo, + DWORD* pCount); + +void ffDetectUsers(FFlist* users, FFstrbuf* error) +{ + WTS_SESSION_INFO_1W* sessionInfo; + DWORD sessionCount; + DWORD level = 1; + + if(!WTSEnumerateSessionsExW(WTS_CURRENT_SERVER_HANDLE, &level, 0, &sessionInfo, &sessionCount)) + { + ffStrbufAppendS(error, "WTSEnumerateSessionsW(WTS_CURRENT_SERVER_HANDLE) failed"); + return; + } + + for (DWORD i = 0; i < sessionCount; i++) + { + WTS_SESSION_INFO_1W* session = &sessionInfo[i]; + if(session->State != WTSActive) + continue; + + FF_STRBUF_AUTO_DESTROY domainName = ffStrbufCreateWS(session->pDomainName); + FF_STRBUF_AUTO_DESTROY userName = ffStrbufCreateWS(session->pUserName); + + ffStrbufInitF((FFstrbuf*)ffListAdd(users), "%s\\%s", domainName.chars, userName.chars); + } + + WTSFreeMemory(sessionInfo); + + if(users->length == 0) + ffStrbufAppendS(error, "Unable to detect users"); +} diff --git a/src/detection/vulkan.c b/src/detection/vulkan.c index a04ab11d2..4119280a7 100644 --- a/src/detection/vulkan.c +++ b/src/detection/vulkan.c @@ -1,9 +1,8 @@ #include "fastfetch.h" +#include "common/thread.h" #include "detection/vulkan.h" #include "detection/gpu/gpu.h" -#include - #ifdef FF_HAVE_VULKAN #include "common/library.h" #include "common/io.h" @@ -25,7 +24,11 @@ static void applyDriverName(VkPhysicalDeviceDriverProperties* properties, FFstrb ffStrbufAppendS(result, properties->driverName); - if(!ffStrSet(properties->driverInfo)) + /* + * Some drivers (android for example) expose a multiline string as driver info. + * It contains too much info anyways, so we just don't append it. + */ + if(!ffStrSet(properties->driverInfo) || strchr(properties->driverInfo, '\n') != NULL) return; ffStrbufAppendS(result, " ["); @@ -35,7 +38,13 @@ static void applyDriverName(VkPhysicalDeviceDriverProperties* properties, FFstrb static const char* detectVulkan(const FFinstance* instance, FFVulkanResult* result) { - FF_LIBRARY_LOAD(vulkan, &instance->config.libVulkan, "dlopen libvulkan"FF_LIBRARY_EXTENSION " failed", "libvulkan"FF_LIBRARY_EXTENSION, 2) + FF_LIBRARY_LOAD(vulkan, &instance->config.libVulkan, "dlopen libvulkan"FF_LIBRARY_EXTENSION " failed", + #ifdef __APPLE__ + "libMoltenVK"FF_LIBRARY_EXTENSION, -1 + #else + "libvulkan"FF_LIBRARY_EXTENSION, 2, "vulkan-1"FF_LIBRARY_EXTENSION, -1 + #endif + ) FF_LIBRARY_LOAD_SYMBOL_MESSAGE(vulkan, vkGetInstanceProcAddr) FF_LIBRARY_LOAD_SYMBOL_MESSAGE(vulkan, vkCreateInstance) FF_LIBRARY_LOAD_SYMBOL_MESSAGE(vulkan, vkDestroyInstance) @@ -85,16 +94,9 @@ static const char* detectVulkan(const FFinstance* instance, FFVulkanResult* resu .pApplicationInfo = &applicationInfo, .enabledLayerCount = 0, .ppEnabledLayerNames = NULL, - - #if defined(__APPLE__) && defined(VK_KHR_portability_enumeration) - .enabledExtensionCount = 1, - .ppEnabledExtensionNames = (const char* const[]) { VK_KHR_PORTABILITY_ENUMERATION_EXTENSION_NAME }, - .flags = VK_INSTANCE_CREATE_ENUMERATE_PORTABILITY_BIT_KHR - #else - .enabledExtensionCount = 0, - .ppEnabledExtensionNames = NULL, - .flags = 0 - #endif + .enabledExtensionCount = 0, + .ppEnabledExtensionNames = NULL, + .flags = 0 }; VkInstance vkInstance; @@ -179,7 +181,7 @@ static const char* detectVulkan(const FFinstance* instance, FFVulkanResult* resu //Add the device to the list of devices shown by the GPU module - //We don't want softare rasterizers to show up as physical gpu + //We don't want software rasterizers to show up as physical gpu if(physicalDeviceProperties.properties.deviceType == VK_PHYSICAL_DEVICE_TYPE_CPU) continue; @@ -217,13 +219,13 @@ static const char* detectVulkan(const FFinstance* instance, FFVulkanResult* resu const FFVulkanResult* ffDetectVulkan(const FFinstance* instance) { static FFVulkanResult 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; @@ -240,6 +242,6 @@ const FFVulkanResult* ffDetectVulkan(const FFinstance* instance) result.error = "fastfetch was compiled without vulkan support"; #endif - pthread_mutex_unlock(&mutex); + ffThreadMutexUnlock(&mutex); return &result; } diff --git a/src/detection/wifi/wifi.h b/src/detection/wifi/wifi.h new file mode 100644 index 000000000..375299a76 --- /dev/null +++ b/src/detection/wifi/wifi.h @@ -0,0 +1,41 @@ +#pragma once + +#ifndef FF_INCLUDED_detection_wifi_wifi +#define FF_INCLUDED_detection_wifi_wifi + +#include "fastfetch.h" + +struct FFWifiInterface +{ + FFstrbuf description; + FFstrbuf status; +}; + +struct FFWifiConnection +{ + FFstrbuf status; + FFstrbuf ssid; + FFstrbuf macAddress; + FFstrbuf phyType; + double signalQuality; // Percentage + double rxRate; + double txRate; +}; + +struct FFWifiSecurity +{ + bool enabled; + bool oneXEnabled; + FFstrbuf algorithm; +}; + +typedef struct FFWifiResult +{ + struct FFWifiInterface inf; + struct FFWifiConnection conn; + struct FFWifiSecurity security; +} FFWifiResult; + +const char* ffDetectWifi(const FFinstance* instance, FFlist* result /*list of FFWifiItem*/); + +#endif diff --git a/src/detection/wifi/wifi_apple.m b/src/detection/wifi/wifi_apple.m new file mode 100644 index 000000000..8d96778f1 --- /dev/null +++ b/src/detection/wifi/wifi_apple.m @@ -0,0 +1,132 @@ +#include "wifi.h" + +#import + +const char* ffDetectWifi(const FFinstance* instance, FFlist* result) +{ + FF_UNUSED(instance); + + NSArray* interfaces = CWWiFiClient.sharedWiFiClient.interfaces; + if(!interfaces) + return "CWWiFiClient.sharedWiFiClient.interfaces is nil"; + + if(interfaces.count == 0) + return "No wifi interfaces found"; + + for(CWInterface* inf in interfaces) + { + FFWifiResult* item = (FFWifiResult*)ffListAdd(result); + ffStrbufInit(&item->inf.description); + ffStrbufInit(&item->inf.status); + ffStrbufInit(&item->conn.status); + ffStrbufInit(&item->conn.ssid); + ffStrbufInit(&item->conn.macAddress); + ffStrbufInit(&item->conn.phyType); + item->conn.signalQuality = 0.0/0.0; + item->conn.rxRate = 0.0/0.0; + item->conn.txRate = 0.0/0.0; + item->security.enabled = false; + item->security.oneXEnabled = false; + ffStrbufInit(&item->security.algorithm); + + ffStrbufAppendS(&item->inf.description, inf.interfaceName.UTF8String); + ffStrbufAppendS(&item->inf.status, inf.powerOn ? "Power On" : "Power Off"); + if(!inf.powerOn) + continue; + + ffStrbufAppendS(&item->conn.status, inf.serviceActive ? "Active" : "Inactive"); + if(!inf.serviceActive) + continue; + + ffStrbufAppendS(&item->conn.ssid, inf.ssid.UTF8String); + ffStrbufAppendS(&item->conn.macAddress, inf.hardwareAddress.UTF8String); + switch(inf.activePHYMode) + { + case kCWPHYModeNone: + ffStrbufAppendS(&item->conn.phyType, "none"); + break; + case kCWPHYMode11a: + ffStrbufAppendS(&item->conn.phyType, "802.11a"); + break; + case kCWPHYMode11b: + ffStrbufAppendS(&item->conn.phyType, "802.11b"); + break; + case kCWPHYMode11g: + ffStrbufAppendS(&item->conn.phyType, "802.11g"); + break; + case kCWPHYMode11n: + ffStrbufAppendS(&item->conn.phyType, "802.11n (Wi-Fi 4)"); + break; + case kCWPHYMode11ac: + ffStrbufAppendS(&item->conn.phyType, "802.11ac (Wi-Fi 5)"); + break; + case kCWPHYMode11ax: + ffStrbufAppendS(&item->conn.phyType, "802.11ax (Wi-Fi 6)"); + break; + case 7 /*kCWPHYMode11be?*/: + ffStrbufAppendS(&item->conn.phyType, "802.11be (Wi-Fi 7)"); + break; + default: + ffStrbufAppendF(&item->conn.phyType, "Unknown (%ld)", inf.activePHYMode); + break; + } + item->conn.signalQuality = inf.rssiValue >= -50 ? 100 : inf.rssiValue <= -100 ? 0 : (inf.rssiValue + 100) * 2; + item->conn.txRate = inf.transmitRate; + item->security.enabled = inf.security != kCWSecurityNone; + switch(inf.security) + { + case kCWSecurityNone: + ffStrbufAppendS(&item->security.algorithm, "None"); + break; + case kCWSecurityWEP: + ffStrbufAppendS(&item->security.algorithm, "WEP"); + break; + case kCWSecurityWPAPersonal: + ffStrbufAppendS(&item->security.algorithm, "WPA Personal"); + break; + case kCWSecurityWPAPersonalMixed: + ffStrbufAppendS(&item->security.algorithm, "WPA Persional Mixed"); + break; + case kCWSecurityWPA2Personal: + ffStrbufAppendS(&item->security.algorithm, "WPA2 Personal"); + break; + case kCWSecurityPersonal: + ffStrbufAppendS(&item->security.algorithm, "Personal"); + break; + case kCWSecurityDynamicWEP: + ffStrbufAppendS(&item->security.algorithm, "Dynamic WEP"); + break; + case kCWSecurityWPAEnterprise: + ffStrbufAppendS(&item->security.algorithm, "WPA Enterprise"); + break; + case kCWSecurityWPAEnterpriseMixed: + ffStrbufAppendS(&item->security.algorithm, "WPA Enterprise Mixed"); + break; + case kCWSecurityWPA2Enterprise: + ffStrbufAppendS(&item->security.algorithm, "WPA2 Enterprise"); + break; + case kCWSecurityEnterprise: + ffStrbufAppendS(&item->security.algorithm, "Enterprise"); + break; + case kCWSecurityWPA3Personal: + ffStrbufAppendS(&item->security.algorithm, "WPA3 Personal"); + break; + case kCWSecurityWPA3Enterprise: + ffStrbufAppendS(&item->security.algorithm, "WPA3 Enterprise"); + break; + case kCWSecurityWPA3Transition: + ffStrbufAppendS(&item->security.algorithm, "WPA3 Transition"); + break; + case 14 /*kCWSecurityOWE*/: + ffStrbufAppendS(&item->security.algorithm, "OWE"); + break; + case 15 /*kCWSecurityOWETransition*/: + ffStrbufAppendS(&item->security.algorithm, "OWE Transition"); + break; + default: + ffStrbufAppendF(&item->security.algorithm, "Unknown (%ld)", inf.security); + break; + } + } + return NULL; +} diff --git a/src/detection/wifi/wifi_linux.c b/src/detection/wifi/wifi_linux.c new file mode 100644 index 000000000..985ba6f74 --- /dev/null +++ b/src/detection/wifi/wifi_linux.c @@ -0,0 +1,61 @@ +#include "wifi.h" + +#include "common/io.h" +#include +#include + +static const char* detectInf(uint32_t ifIndex, FFWifiResult* wifi) +{ + //TODO: play with netlink + FF_UNUSED(ifIndex, wifi); + return "Unimplemented"; +} + +const char* ffDetectWifi(const FFinstance* instance, FFlist* result) +{ + FF_UNUSED(instance); + + struct if_nameindex* infs = if_nameindex(); + if(!infs) + return "if_nameindex() failed"; + + FFstrbuf path; + ffStrbufInit(&path); + + for(struct if_nameindex* i = infs; !(i->if_index == 0 && i->if_name == NULL); ++i) + { + ffStrbufSetF(&path, "/sys/class/net/%s/phy80211", i->if_name); + if(!ffFileExists(path.chars, S_IFDIR)) + continue; + + FFWifiResult* item = (FFWifiResult*)ffListAdd(result); + ffStrbufInitS(&item->inf.description, i->if_name); + ffStrbufInit(&item->inf.status); + ffStrbufInit(&item->conn.status); + ffStrbufInit(&item->conn.ssid); + ffStrbufInit(&item->conn.macAddress); + ffStrbufInit(&item->conn.phyType); + item->conn.signalQuality = 0.0/0.0; + item->conn.rxRate = 0.0/0.0; + item->conn.txRate = 0.0/0.0; + item->security.enabled = false; + item->security.oneXEnabled = false; + ffStrbufInit(&item->security.algorithm); + + ffStrbufSetF(&path, "/sys/class/net/%s/operstate", i->if_name); + if(!ffAppendFileBuffer(path.chars, &item->inf.status) || !ffStrbufEqualS(&item->inf.status, "up")) + continue; + + ffStrbufSetF(&path, "/sys/class/net/%s/address", i->if_name); + ffAppendFileBuffer(path.chars, &item->conn.macAddress); + + detectInf(i->if_index, item); + } + if_freenameindex(infs); + ffStrbufDestroy(&path); + + if(result->length == 0) + return "No wifi interfaces found"; + + return NULL; +} diff --git a/src/detection/wifi/wifi_nosupport.c b/src/detection/wifi/wifi_nosupport.c new file mode 100644 index 000000000..d09116c9b --- /dev/null +++ b/src/detection/wifi/wifi_nosupport.c @@ -0,0 +1,7 @@ +#include "wifi.h" + +const char* ffDetectWifi(const FFinstance* instance, FFlist* result) +{ + FF_UNUSED(instance, result); + return "Not supported on this platform"; +} diff --git a/src/detection/wifi/wifi_windows.c b/src/detection/wifi/wifi_windows.c new file mode 100644 index 000000000..f113197f5 --- /dev/null +++ b/src/detection/wifi/wifi_windows.c @@ -0,0 +1,262 @@ +#include "wifi.h" +#include "common/library.h" +#include "util/windows/unicode.h" + +#include + +#pragma GCC diagnostic push +#pragma GCC diagnostic ignored "-Wswitch" + +static void convertIfStateToString(WLAN_INTERFACE_STATE state, FFstrbuf* result) +{ + switch (state) { + case wlan_interface_state_not_ready: + ffStrbufAppendS(result, "Not ready"); + break; + case wlan_interface_state_connected: + ffStrbufAppendS(result, "Connected"); + break; + case wlan_interface_state_ad_hoc_network_formed: + ffStrbufAppendS(result, "First node in a ad hoc network"); + break; + case wlan_interface_state_disconnecting: + ffStrbufAppendS(result, "Disconnecting"); + break; + case wlan_interface_state_disconnected: + ffStrbufAppendS(result, "Not connected"); + break; + case wlan_interface_state_associating: + ffStrbufAppendS(result, "Attempting to associate with a network"); + break; + case wlan_interface_state_discovering: + ffStrbufAppendS(result, "Auto configuration is discovering settings for the network"); + break; + case wlan_interface_state_authenticating: + ffStrbufAppendS(result, "In process of authenticating"); + break; + default: + ffStrbufAppendS(result, "Unknown"); + break; + } +} + +const char* ffDetectWifi(const FFinstance* instance, FFlist* result) +{ + FF_LIBRARY_LOAD(wlanapi, &instance->config.libcJSON, "dlopen wlanapi"FF_LIBRARY_EXTENSION" failed", "wlanapi"FF_LIBRARY_EXTENSION, 1) + FF_LIBRARY_LOAD_SYMBOL_MESSAGE(wlanapi, WlanOpenHandle) + FF_LIBRARY_LOAD_SYMBOL_MESSAGE(wlanapi, WlanEnumInterfaces) + FF_LIBRARY_LOAD_SYMBOL_MESSAGE(wlanapi, WlanQueryInterface) + FF_LIBRARY_LOAD_SYMBOL_MESSAGE(wlanapi, WlanFreeMemory) + FF_LIBRARY_LOAD_SYMBOL_MESSAGE(wlanapi, WlanCloseHandle) + + DWORD curVersion; + HANDLE hClient = NULL; + WLAN_INTERFACE_INFO_LIST* ifList = NULL; + const char* error = NULL; + + if(ffWlanOpenHandle(2, NULL, &curVersion, &hClient) != ERROR_SUCCESS) + { + error = "WlanOpenHandle() failed"; + goto exit; + } + + if(ffWlanEnumInterfaces(hClient, NULL, &ifList) != ERROR_SUCCESS) + { + error = "WlanEnumInterfaces() failed"; + goto exit; + } + + if(ifList->dwNumberOfItems == 0) + { + error = "No wifi interfaces found"; + goto exit; + } + + for(uint32_t index = 0; index < ifList->dwNumberOfItems; ++index) + { + WLAN_INTERFACE_INFO* ifInfo = (WLAN_INTERFACE_INFO*)&ifList->InterfaceInfo[index]; + + FFWifiResult* item = (FFWifiResult*)ffListAdd(result); + ffStrbufInit(&item->inf.description); + ffStrbufInit(&item->inf.status); + ffStrbufInit(&item->conn.status); + ffStrbufInit(&item->conn.ssid); + ffStrbufInit(&item->conn.macAddress); + ffStrbufInit(&item->conn.phyType); + item->conn.signalQuality = 0.0/0.0; + item->conn.rxRate = 0.0/0.0; + item->conn.txRate = 0.0/0.0; + item->security.enabled = false; + item->security.oneXEnabled = false; + ffStrbufInit(&item->security.algorithm); + + ffStrbufSetWS(&item->inf.description, ifInfo->strInterfaceDescription); + convertIfStateToString(ifInfo->isState, &item->inf.status); + + if(ifInfo->isState != wlan_interface_state_connected) + continue; + + DWORD connectInfoSize = sizeof(WLAN_CONNECTION_ATTRIBUTES); + WLAN_OPCODE_VALUE_TYPE opCode = wlan_opcode_value_type_invalid; + WLAN_CONNECTION_ATTRIBUTES* connInfo = NULL; + + if(ffWlanQueryInterface(hClient, + &ifInfo->InterfaceGuid, + wlan_intf_opcode_current_connection, + NULL, + &connectInfoSize, + (PVOID*)&connInfo, + &opCode) != ERROR_SUCCESS + ) continue; + + convertIfStateToString(connInfo->isState, &item->conn.status); + ffStrbufAppendNS(&item->conn.ssid, + connInfo->wlanAssociationAttributes.dot11Ssid.uSSIDLength, + (const char *)connInfo->wlanAssociationAttributes.dot11Ssid.ucSSID); + + for (size_t i = 0; i < sizeof(connInfo->wlanAssociationAttributes.dot11Bssid); i++) + ffStrbufAppendF(&item->conn.macAddress, "%.2X-", connInfo->wlanAssociationAttributes.dot11Bssid[i]); + ffStrbufTrimRight(&item->conn.macAddress, '-'); + + switch (connInfo->wlanAssociationAttributes.dot11PhyType) + { + case dot11_phy_type_fhss: + ffStrbufAppendS(&item->conn.phyType, "802.11 (FHSS)"); + break; + case dot11_phy_type_dsss: + ffStrbufAppendS(&item->conn.phyType, "802.11 (DSSS)"); + break; + case dot11_phy_type_irbaseband: + ffStrbufAppendS(&item->conn.phyType, "802.11 (IR)"); + break; + case dot11_phy_type_ofdm: + ffStrbufAppendS(&item->conn.phyType, "802.11a"); + break; + case dot11_phy_type_hrdsss: + ffStrbufAppendS(&item->conn.phyType, "802.11b"); + break; + case dot11_phy_type_erp: + ffStrbufAppendS(&item->conn.phyType, "802.11g"); + break; + case dot11_phy_type_ht: + ffStrbufAppendS(&item->conn.phyType, "802.11n (Wi-Fi 4)"); + break; + case 8 /*dot11_phy_type_vht*/: + ffStrbufAppendS(&item->conn.phyType, "802.11ac (Wi-Fi 5)"); + break; + case 9 /*dot11_phy_type_dmg*/: + ffStrbufAppendS(&item->conn.phyType, "802.11ad (WiGig)"); + break; + case 10 /*dot11_phy_type_he*/: + ffStrbufAppendS(&item->conn.phyType, "802.11ax (Wi-Fi 6)"); + break; + case 11 /*dot11_phy_type_eht*/: + ffStrbufAppendS(&item->conn.phyType, "802.11be (Wi-Fi 7)"); + break; + default: + ffStrbufAppendF(&item->conn.phyType, "Unknown (%u)", (unsigned)connInfo->wlanAssociationAttributes.dot11PhyType); + break; + } + + item->conn.signalQuality = connInfo->wlanAssociationAttributes.wlanSignalQuality; + item->conn.rxRate = connInfo->wlanAssociationAttributes.ulRxRate; + item->conn.txRate = connInfo->wlanAssociationAttributes.ulTxRate; + + item->security.enabled = connInfo->wlanSecurityAttributes.bSecurityEnabled; + item->security.oneXEnabled = connInfo->wlanSecurityAttributes.bOneXEnabled; + switch (connInfo->wlanSecurityAttributes.dot11AuthAlgorithm) + { + case DOT11_AUTH_ALGO_80211_OPEN: + ffStrbufAppendS(&item->security.algorithm, "802.11 Open"); + break; + case DOT11_AUTH_ALGO_80211_SHARED_KEY: + ffStrbufAppendS(&item->security.algorithm, "802.11 Shared"); + break; + case DOT11_AUTH_ALGO_WPA: + ffStrbufAppendS(&item->security.algorithm, "WPA"); + break; + case DOT11_AUTH_ALGO_WPA_PSK: + ffStrbufAppendS(&item->security.algorithm, "WPA-PSK"); + break; + case DOT11_AUTH_ALGO_WPA_NONE: + ffStrbufAppendS(&item->security.algorithm, "WPA-None"); + break; + case DOT11_AUTH_ALGO_RSNA: + ffStrbufAppendS(&item->security.algorithm, "RSNA"); + break; + case DOT11_AUTH_ALGO_RSNA_PSK: + ffStrbufAppendS(&item->security.algorithm, "RSNA with PSK"); + break; + case 8 /* DOT11_AUTH_ALGO_WPA3 */: + ffStrbufAppendS(&item->security.algorithm, "WPA3"); + break; + case 9 /* DOT11_AUTH_ALGO_WPA3_SAE */: + ffStrbufAppendS(&item->security.algorithm, "WPA3-SAE"); + break; + case 10 /* DOT11_AUTH_ALGO_OWE */: + ffStrbufAppendS(&item->security.algorithm, "OWE"); + break; + case 11 /* DOT11_AUTH_ALGO_WPA3_ENT */: + ffStrbufAppendS(&item->security.algorithm, "OWE-ENT"); + break; + default: + ffStrbufAppendF(&item->security.algorithm, "Unknown (%u)", (unsigned)connInfo->wlanSecurityAttributes.dot11AuthAlgorithm); + break; + } + ffStrbufAppendS(&item->security.algorithm, " - "); + switch (connInfo->wlanSecurityAttributes.dot11CipherAlgorithm) + { + case DOT11_CIPHER_ALGO_NONE: + ffStrbufAppendS(&item->security.algorithm, "None"); + break; + case DOT11_CIPHER_ALGO_WEP40: + ffStrbufAppendS(&item->security.algorithm, "WEP-40"); + break; + case DOT11_CIPHER_ALGO_TKIP: + ffStrbufAppendS(&item->security.algorithm, "TKIP"); + break; + case DOT11_CIPHER_ALGO_CCMP: + ffStrbufAppendS(&item->security.algorithm, "CCMP"); + break; + case DOT11_CIPHER_ALGO_WEP104: + ffStrbufAppendS(&item->security.algorithm, "WEP-104"); + break; + case 0x06 /* DOT11_CIPHER_ALGO_BIP */: + ffStrbufAppendS(&item->security.algorithm, "BIP-CMAC-128"); + break; + case 0x08 /* DOT11_CIPHER_ALGO_GCMP */: + ffStrbufAppendS(&item->security.algorithm, "GCMP-128"); + break; + case 0x09 /* DOT11_CIPHER_ALGO_GCMP_256 */: + ffStrbufAppendS(&item->security.algorithm, "GCMP-256"); + break; + case 0x0a /* DOT11_CIPHER_ALGO_CCMP_256 */: + ffStrbufAppendS(&item->security.algorithm, "CCMP-256"); + break; + case 0x0b /* DOT11_CIPHER_ALGO_BIP_GMAC_128 */: + ffStrbufAppendS(&item->security.algorithm, "BIP-GMAC-128"); + break; + case 0x0c /* DOT11_CIPHER_ALGO_BIP_GMAC_256 */: + ffStrbufAppendS(&item->security.algorithm, "BIP-GMAC-256"); + break; + case 0x0d /* DOT11_CIPHER_ALGO_BIP_CMAC_256 */: + ffStrbufAppendS(&item->security.algorithm, "BIP-CMAC-256"); + break; + case DOT11_CIPHER_ALGO_WEP: + ffStrbufAppendS(&item->security.algorithm, "WEP"); + break; + default: + ffStrbufAppendF(&item->security.algorithm, "Unknown (%u)", (unsigned)connInfo->wlanSecurityAttributes.dot11CipherAlgorithm); + break; + } + ffWlanFreeMemory(connInfo); + } + +exit: + if(ifList) ffWlanFreeMemory(ifList); + if(hClient) ffWlanCloseHandle(hClient, NULL); + dlclose(wlanapi); + return error; +} + +#pragma GCC diagnostic pop diff --git a/src/detection/wmtheme/wmtheme_linux.c b/src/detection/wmtheme/wmtheme_linux.c index 0650d2f4a..19765c79a 100644 --- a/src/detection/wmtheme/wmtheme_linux.c +++ b/src/detection/wmtheme/wmtheme_linux.c @@ -224,6 +224,6 @@ bool ffDetectWmTheme(FFinstance* instance, FFstrbuf* themeOrError) return detectOpenbox(instance, &wm->dePrettyName, themeOrError); ffStrbufAppendS(themeOrError, "Unknown WM: "); - ffStrbufAppend(themeOrError, &wm->dePrettyName); + ffStrbufAppend(themeOrError, &wm->wmPrettyName); return false; } diff --git a/src/detection/wmtheme/wmtheme_android.c b/src/detection/wmtheme/wmtheme_nosupport.c similarity index 71% rename from src/detection/wmtheme/wmtheme_android.c rename to src/detection/wmtheme/wmtheme_nosupport.c index 7d9da9a5d..5dc5691d3 100644 --- a/src/detection/wmtheme/wmtheme_android.c +++ b/src/detection/wmtheme/wmtheme_nosupport.c @@ -5,6 +5,6 @@ bool ffDetectWmTheme(FFinstance* instance, FFstrbuf* themeOrError) { FF_UNUSED(instance); - ffStrbufAppendS(themeOrError, "WM theme detection is not supported on Android"); + ffStrbufAppendS(themeOrError, "Not supported on this platform"); return false; } diff --git a/src/detection/wmtheme/wmtheme_windows.c b/src/detection/wmtheme/wmtheme_windows.c new file mode 100644 index 000000000..cf070be69 --- /dev/null +++ b/src/detection/wmtheme/wmtheme_windows.c @@ -0,0 +1,130 @@ +#include "fastfetch.h" +#include "wmtheme.h" +#include "util/windows/registry.h" + +const char* colorHexToString(DWORD hex) +{ + switch(hex) + { + case 0x696cc3: return "Yellow gold"; + case 0xff8c00: return "Gold"; + case 0xf7630c: return "Orange bright"; + case 0xca5010: return "Orange dark"; + case 0xda3b01: return "Rust"; + case 0xef6950: return "Pale rust"; + case 0xd13438: return "Brick red"; + case 0xff4343: return "Mod red"; + case 0xe74856: return "Pale red"; + case 0xe81123: return "Red"; + case 0xea005e: return "Rose bright"; + case 0xc30052: return "Rose"; + case 0xe3008c: return "Plum light"; + case 0xbf0077: return "Plum"; + case 0xc239b3: return "Orchid light"; + case 0x9a0089: return "Orchid"; + case 0x0078d4: return "Blue"; + case 0x0063b1: return "Navy blue"; + case 0x8d8bd7: return "Purple shadow"; + case 0x6b69d6: return "Purple shadow dark"; + case 0x8764b8: return "Iris pastel"; + case 0x744da9: return "Iris Spring"; + case 0xb146c2: return "Violet red light"; + case 0x881798: return "Violet red"; + case 0x0099bc: return "Cool blue bright"; + case 0x2d7d9a: return "Cool blue"; + case 0x00b7c3: return "Seafoam"; + case 0x038387: return "Seafoam teal"; + case 0x00b294: return "Mint light"; + case 0x018574: return "Mint dark"; + case 0x00cc6a: return "Turf green"; + case 0x10893e: return "Sport green"; + case 0x7a7574: return "Gray"; + case 0x5d5a58: return "Gray brown"; + case 0x68768a: return "Steel blue"; + case 0x515c6b: return "Metal blue"; + case 0x567c73: return "Pale moss"; + case 0x486860: return "Moss"; + case 0x498205: return "Meadow green"; + case 0x107c10: return "Green"; + case 0x767676: return "Overcast"; + case 0x4c4a48: return "Storm"; + case 0x69797e: return "Blue gray"; + case 0x4a5459: return "Gray dark"; + case 0x647c64: return "Liddy green"; + case 0x4c574e: return "Sage"; + case 0x807143: return "Camouflage desert"; + case 0x766c59: return "Camouflage"; + case 0x000000: return "Black"; + case 0xFFFFFF: return "White"; + default: return NULL; + } +} + +bool ffDetectWmTheme(FFinstance* instance, FFstrbuf* themeOrError) +{ + FF_UNUSED(instance); + + do { + uint32_t rgbColor; + uint32_t bgrColor; + DWORD bufSize = sizeof(bgrColor); + if(RegGetValueW(HKEY_CURRENT_USER, L"Software\\Microsoft\\Windows\\DWM", L"AccentColor", RRF_RT_REG_DWORD, NULL, &bgrColor, &bufSize) == ERROR_SUCCESS) + { + ffStrbufAppendS(themeOrError, "Accent Color - "); + rgbColor = ((bgrColor & 0xFF) << 16) | (bgrColor & 0xFF00) | ((bgrColor >> 16) & 0xFF); + + } + else if(RegGetValueW(HKEY_CURRENT_USER, L"Software\\Microsoft\\Windows\\DWM", L"ColorizationColor", RRF_RT_REG_DWORD, NULL, &rgbColor, &bufSize) == ERROR_SUCCESS) + { + ffStrbufAppendS(themeOrError, "Colorization Color - "); + rgbColor &= 0xFFFFFF; + } + else + break; + + const char* text = colorHexToString(rgbColor); + if(text) + ffStrbufAppendS(themeOrError, text); + else + ffStrbufAppendF(themeOrError, "#%06lX", (long)rgbColor); + } while(false); + + FF_HKEY_AUTO_DESTROY hKey = NULL; + if(ffRegOpenKeyForRead(HKEY_CURRENT_USER, L"SOFTWARE\\Microsoft\\Windows\\CurrentVersion\\Themes\\Personalize", &hKey, NULL)) + { + uint32_t value = 1; + if(ffRegReadUint(hKey, L"SystemUsesLightTheme", &value, NULL)) + { + if(themeOrError->length > 0) ffStrbufAppendS(themeOrError, ", "); + ffStrbufAppendF(themeOrError, "System - %s", value ? "Light" : "Dark"); + } + + if(ffRegReadUint(hKey, L"AppsUseLightTheme", &value, NULL)) + { + if(themeOrError->length > 0) ffStrbufAppendS(themeOrError, ", "); + ffStrbufAppendF(themeOrError, "Apps - %s", value ? "Light" : "Dark"); + } + } + else if(ffRegOpenKeyForRead(HKEY_CURRENT_USER, L"SOFTWARE\\Microsoft\\Windows\\CurrentVersion\\Themes", &hKey, NULL)) + { + FF_STRBUF_AUTO_DESTROY theme; + ffStrbufInit(&theme); + if(ffRegReadStrbuf(hKey, L"CurrentTheme", &theme, NULL)) + { + ffStrbufSubstrBeforeLastC(&theme, '.'); + ffStrbufSubstrAfterLastC(&theme, '\\'); + if(isalpha(theme.chars[0])) + theme.chars[0] = (char)toupper(theme.chars[0]); + + if(themeOrError->length > 0) ffStrbufAppendS(themeOrError, ", "); + ffStrbufAppendF(themeOrError, "Theme - %s", theme.chars); + } + } + + if(themeOrError->length == 0) + { + ffStrbufAppendS(themeOrError, "Failed to find current theme"); + return false; + } + return true; +} diff --git a/src/fastfetch.c b/src/fastfetch.c index 75a6d0a06..ac74ab500 100644 --- a/src/fastfetch.c +++ b/src/fastfetch.c @@ -3,12 +3,19 @@ #include "common/printing.h" #include "common/parsing.h" #include "common/io.h" +#include "common/time.h" #include #include #include -#include -#include +#include + +#ifdef WIN32 + #include "util/windows/getline.h" + #include +#else + #include +#endif typedef struct CustomValue { @@ -45,13 +52,13 @@ static void constructAndPrintCommandHelpFormat(const char* name, const char* def static inline void printCommandHelp(const char* command) { if(command == NULL) - fputs(FASTFETCH_DATATEXT_HELP, stdout); + puts(FASTFETCH_DATATEXT_HELP); else if(strcasecmp(command, "c") == 0 || strcasecmp(command, "color") == 0) - fputs(FASTFETCH_DATATEXT_HELP_COLOR, stdout); + puts(FASTFETCH_DATATEXT_HELP_COLOR); else if(strcasecmp(command, "format") == 0) - fputs(FASTFETCH_DATATEXT_HELP_FORMAT, stdout); + puts(FASTFETCH_DATATEXT_HELP_FORMAT); else if(strcasecmp(command, "load-config") == 0 || strcasecmp(command, "loadconfig") == 0 || strcasecmp(command, "config") == 0) - fputs(FASTFETCH_DATATEXT_HELP_CONFIG, stdout); + puts(FASTFETCH_DATATEXT_HELP_CONFIG); else if(strcasecmp(command, "os-format") == 0) { constructAndPrintCommandHelpFormat("os", "{3} {12}", 12, @@ -71,24 +78,42 @@ static inline void printCommandHelp(const char* command) } else if(strcasecmp(command, "host-format") == 0) { - constructAndPrintCommandHelpFormat("host", "{2} {3}", 15, + constructAndPrintCommandHelpFormat("host", "{2} {3}", 8, "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" ); } + else if(strcasecmp(command, "bios-format") == 0) + { + constructAndPrintCommandHelpFormat("bios", "{2} {3}", 4, + "bios date", + "bios release", + "bios vendor", + "bios version" + ); + } + else if(strcasecmp(command, "board-format") == 0) + { + constructAndPrintCommandHelpFormat("board", "{2} {3}", 3, + "board name", + "board vendor", + "board version" + ); + } + else if(strcasecmp(command, "chassis-format") == 0) + { + constructAndPrintCommandHelpFormat("chassis", "{2} {3}", 4, + "chassis type", + "chassis vendor", + "chassis version" + ); + } else if(strcasecmp(command, "kernel-format") == 0) { constructAndPrintCommandHelpFormat("kernel", "{2}", 3, @@ -114,13 +139,14 @@ static inline void printCommandHelp(const char* command) } else if(strcasecmp(command, "packages-format") == 0) { - constructAndPrintCommandHelpFormat("packages", "{2} (pacman){?3}[{3}]{?}, {4} (dpkg), {5} (rpm), {6} (emerge), {7} (xbps), {8} (nix-system), {9} (nix-user), {10} (nix-default), {11} (apk), {12} (pkg), {13} (flatpak), {14} (snap), {15} (brew), {16} (port)", 16, + constructAndPrintCommandHelpFormat("packages", "{2} (pacman){?3}[{3}]{?}, {4} (dpkg), {5} (rpm), {6} (emerge), {7} (eopkg), {8} (xbps), {9} (nix-system), {10} (nix-user), {11} (nix-default), {12} (apk), {13} (pkg), {14} (flatpak), {15} (snap), {16} (brew), {17} (brew-cask), {18} (port), {19} (scoop), {20} (choco)", 20, "Number of all packages", "Number of pacman packages", "Pacman branch on manjaro", "Number of dpkg packages", "Number of rpm packages", "Number of emerge packages", + "Number of eopkg packages", "Number of xbps packages", "Number of nix-system packages", "Number of nix-user packages", @@ -130,7 +156,10 @@ static inline void printCommandHelp(const char* command) "Number of flatpak packages", "Number of snap packages", "Number of brew packages", - "Number of macports packages" + "Number of brew-cask packages", + "Number of macports packages", + "Number of scoop packages", + "Number of choco packages" ); } else if(strcasecmp(command, "shell-format") == 0) @@ -284,11 +313,16 @@ static inline void printCommandHelp(const char* command) } else if(strcasecmp(command, "disk-format") == 0) { - constructAndPrintCommandHelpFormat("disk", "{}GiB / {}GiB ({4}%)", 4, - "Used size", - "Total size", - "Percentage used", - "Num files" + constructAndPrintCommandHelpFormat("disk", "{1} / {2} ({3}%)", 9, + "Size used", + "Size total", + "Size percentage", + "Files used", + "Files total", + "Files percentage", + "True if removable volume", + "True if hidden volume", + "Filesystem" ); } else if(strcasecmp(command, "battery-format") == 0) @@ -329,6 +363,23 @@ static inline void printCommandHelp(const char* command) "Public IP address" ); } + else if(strcasecmp(command, "wifi-format") == 0) + { + constructAndPrintCommandHelpFormat("wifi", "{4} - {6}", 3, + "Interface description", + "Interface status", + "Connection status", + "Connection SSID", + "Connection mac address", + "Connection PHY type", + "Connection signal quality (percentage)", + "Connection RX rate", + "Connection TX rate", + "Security enabled", + "Security 802.1X enabled", + "Security algorithm" + ); + } else if(strcasecmp(command, "player-format") == 0) { constructAndPrintCommandHelpFormat("player", "{}", 4, @@ -403,6 +454,7 @@ static inline void printCommandHelp(const char* command) static inline void listAvailablePresetsFromFolder(FFstrbuf* folder, uint8_t indentation, const char* folderName) { + #ifndef _WIN32 DIR* dir = opendir(folder->chars); if(dir == NULL) return; @@ -418,7 +470,6 @@ static inline void listAvailablePresetsFromFolder(FFstrbuf* folder, uint8_t inde { if(entry->d_type == DT_DIR) { - if(strcmp(entry->d_name, ".") == 0 || strcmp(entry->d_name, "..") == 0) continue; @@ -436,6 +487,40 @@ static inline void listAvailablePresetsFromFolder(FFstrbuf* folder, uint8_t inde } closedir(dir); + #else + uint32_t folderLength = folder->length; + + if(folderName != NULL) + printf("%s/\n", folderName); + + ffStrbufAppendC(folder, '*'); + WIN32_FIND_DATAA entry; + HANDLE hFind = FindFirstFileA(folder->chars, &entry); + if(hFind == INVALID_HANDLE_VALUE) + return; + + do + { + if (entry.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) + { + if(strcmp(entry.cFileName, ".") == 0 || strcmp(entry.cFileName, "..") == 0) + continue; + + ffStrbufSubstrBefore(folder, folderLength); + ffStrbufAppendS(folder, entry.cFileName); + ffStrbufAppendC(folder, '/'); + listAvailablePresetsFromFolder(folder, (uint8_t) (indentation + 1), entry.cFileName); + ffStrbufSubstrBefore(folder, folderLength); + continue; + } + + for(uint8_t i = 0; i < indentation; i++) + fputs(" | ", stdout); + + puts(entry.cFileName); + } while (FindNextFileA(hFind, &entry)); + FindClose(hFind); + #endif } static inline void listAvailablePresets(FFinstance* instance) @@ -592,79 +677,36 @@ static void optionParseString(const char* key, const char* value, FFstrbuf* buff ffStrbufSetS(buffer, value); } +static inline bool startsWith(const char* str, const char* compareTo) +{ + return strncasecmp(str, compareTo, strlen(compareTo)) == 0; +} + static void optionParseColor(const char* key, const char* value, FFstrbuf* buffer) { optionCheckString(key, value, buffer); - static const char reset[] = "reset_"; - static const char bright[] = "bright_"; - - static const char black[] = "black"; - static const char red[] = "red"; - static const char green[] = "green"; - static const char yellow[] = "yellow"; - static const char blue[] = "blue"; - static const char magenta[] = "magenta"; - static const char cyan[] = "cyan"; - static const char white[] = "white"; - while(*value != '\0') { - if(strncasecmp(value, reset, sizeof(reset) - 1) == 0) - { - ffStrbufAppendS(buffer, "0;"); - value += sizeof(reset) - 1; - } - else if(strncasecmp(value, bright, sizeof(bright) - 1) == 0) - { - ffStrbufAppendS(buffer, "1;"); - value += sizeof(bright) - 1; - } - else if(strncasecmp(value, black, sizeof(black) - 1) == 0) - { - ffStrbufAppendS(buffer, "30"); - value += sizeof(black) - 1; - } - else if(strncasecmp(value, red, sizeof(red) - 1) == 0) - { - ffStrbufAppendS(buffer, "31"); - value += sizeof(red) - 1; - } - else if(strncasecmp(value, green, sizeof(green) - 1) == 0) - { - ffStrbufAppendS(buffer, "32"); - value += sizeof(green) - 1; - } - else if(strncasecmp(value, yellow, sizeof(yellow) - 1) == 0) - { - ffStrbufAppendS(buffer, "33"); - value += sizeof(yellow) - 1; - } - else if(strncasecmp(value, blue, sizeof(blue) - 1) == 0) - { - ffStrbufAppendS(buffer, "34"); - value += sizeof(blue) - 1; - } - else if(strncasecmp(value, magenta, sizeof(magenta) - 1) == 0) - { - ffStrbufAppendS(buffer, "35"); - value += sizeof(magenta) - 1; - } - else if(strncasecmp(value, cyan, sizeof(cyan) - 1) == 0) - { - ffStrbufAppendS(buffer, "36"); - value += sizeof(cyan) - 1; - } - else if(strncasecmp(value, white, sizeof(white) - 1) == 0) - { - ffStrbufAppendS(buffer, "37"); - value += sizeof(white) - 1; - } - else + #define FF_APPEND_COLOR_CODE_COND(prefix, code) \ + if(startsWith(value, #prefix)) { ffStrbufAppendS(buffer, code); value += strlen(#prefix); } + + FF_APPEND_COLOR_CODE_COND(reset_, "0;") + else FF_APPEND_COLOR_CODE_COND(bright_, "1;") + else FF_APPEND_COLOR_CODE_COND(black, "30") + else FF_APPEND_COLOR_CODE_COND(red, "31") + else FF_APPEND_COLOR_CODE_COND(green, "32") + else FF_APPEND_COLOR_CODE_COND(yellow, "33") + else FF_APPEND_COLOR_CODE_COND(blue, "34") + else FF_APPEND_COLOR_CODE_COND(magenta, "35") + else FF_APPEND_COLOR_CODE_COND(cyan, "36") + else FF_APPEND_COLOR_CODE_COND(white, "37") { ffStrbufAppendC(buffer, *value); ++value; } + + #undef FF_APPEND_COLOR_CODE_COND } } @@ -746,6 +788,40 @@ static void optionParseEnum(const char* argumentKey, const char* requestedKey, v exit(478); } +static bool optionParseModuleArgs(const char* argumentKey, const char* value, const char* moduleName, struct FFModuleArgs* result) +{ + const char* pkey = argumentKey; + if(!(pkey[0] == '-' && pkey[1] == '-')) + return false; + + pkey += 2; + uint32_t moduleNameLen = (uint32_t)strlen(moduleName); + if(strncasecmp(pkey, moduleName, moduleNameLen) != 0) + return false; + + pkey += moduleNameLen; + if(pkey[0] != '-') + return false; + + pkey += 1; + if(strcasecmp(pkey, "key") == 0) + { + optionParseString(argumentKey, value, &result->key); + return true; + } + else if(strcasecmp(pkey, "format") == 0) + { + optionParseString(argumentKey, value, &result->outputFormat); + return true; + } + else if(strcasecmp(pkey, "error") == 0) + { + optionParseString(argumentKey, value, &result->errorFormat); + return true; + } + return false; +} + static void parseOption(FFinstance* instance, FFdata* data, const char* key, const char* value) { /////////////////////// @@ -767,50 +843,62 @@ static void parseOption(FFinstance* instance, FFdata* data, const char* key, con puts(FASTFETCH_PROJECT_VERSION); exit(0); } - else if(strcasecmp(key, "--print-config-system") == 0) + else if(startsWith(key, "--print")) { - puts(FASTFETCH_DATATEXT_CONFIG_SYSTEM); - exit(0); + const char* subkey = key + strlen("--print"); + if(strcasecmp(subkey, "-config-system") == 0) + { + puts(FASTFETCH_DATATEXT_CONFIG_SYSTEM); + exit(0); + } + else if(strcasecmp(subkey, "-config-user") == 0) + { + puts(FASTFETCH_DATATEXT_CONFIG_USER); + exit(0); + } + else if(strcasecmp(subkey, "-structure") == 0) + { + puts(FASTFETCH_DATATEXT_STRUCTURE); + exit(0); + } + else if(strcasecmp(subkey, "-logos") == 0) + { + ffLogoBuiltinPrint(instance); + exit(0); + } + else + goto error; } - else if(strcasecmp(key, "--print-config-user") == 0) + else if(startsWith(key, "--list")) { - puts(FASTFETCH_DATATEXT_CONFIG_USER); - exit(0); - } - else if(strcasecmp(key, "--print-structure") == 0) - { - puts(FASTFETCH_DATATEXT_STRUCTURE); - exit(0); - } - else if(strcasecmp(key, "--list-modules") == 0) - { - puts(FASTFETCH_DATATEXT_MODULES); - exit(0); - } - else if(strcasecmp(key, "--list-presets") == 0) - { - listAvailablePresets(instance); - exit(0); - } - else if(strcasecmp(key, "--list-features") == 0) - { - ffListFeatures(); - exit(0); - } - else if(strcasecmp(key, "--list-logos") == 0) - { - ffLogoBuiltinList(); - exit(0); - } - else if(strcasecmp(key, "--list-logos-autocompletion") == 0) - { - ffLogoBuiltinListAutocompletion(); - exit(0); - } - else if(strcasecmp(key, "--print-logos") == 0) - { - ffLogoBuiltinPrint(instance); - exit(0); + const char* subkey = key + strlen("--list"); + if(strcasecmp(subkey, "-modules") == 0) + { + puts(FASTFETCH_DATATEXT_MODULES); + exit(0); + } + else if(strcasecmp(subkey, "-presets") == 0) + { + listAvailablePresets(instance); + exit(0); + } + else if(strcasecmp(subkey, "-features") == 0) + { + ffListFeatures(); + exit(0); + } + else if(strcasecmp(subkey, "-logos") == 0) + { + ffLogoBuiltinList(); + exit(0); + } + else if(strcasecmp(subkey, "-logos-autocompletion") == 0) + { + ffLogoBuiltinListAutocompletion(); + exit(0); + } + else + goto error; } /////////////////// @@ -818,20 +906,18 @@ static void parseOption(FFinstance* instance, FFdata* data, const char* key, con /////////////////// else if(strcasecmp(key, "-r") == 0 || strcasecmp(key, "--recache") == 0) - { - //Set cacheSave as well, because the user expects the values to be cached when expliciting using --recache instance->config.recache = optionParseBoolean(value); - instance->config.cacheSave = instance->config.recache; - } else if(strcasecmp(key, "--nocache") == 0) - { - instance->config.recache = optionParseBoolean(value); - instance->config.cacheSave = false; - } + fputs("`--nocache` are obsoleted. Caching functions other than image caching are removed.\n\n", stderr); else if(strcasecmp(key, "--load-config") == 0) optionParseConfigFile(instance, data, key, value); - else if(strcasecmp(key, "--multithreading") == 0) + else if(strcasecmp(key, "--thread") == 0 || strcasecmp(key, "--multithreading") == 0) instance->config.multithreading = optionParseBoolean(value); + else if(strcasecmp(key, "--stat") == 0) + { + if((instance->config.stat = optionParseBoolean(value))) + instance->config.showErrors = true; + } else if(strcasecmp(key, "--allow-slow-operations") == 0) instance->config.allowSlowOperations = optionParseBoolean(value); else if(strcasecmp(key, "--escape-bedrock") == 0) @@ -856,74 +942,109 @@ static void parseOption(FFinstance* instance, FFdata* data, const char* key, con instance->config.logo.paddingLeft = 0; } } - else if(strcasecmp(key, "--logo-type") == 0) + else if(startsWith(key, "--logo")) { - optionParseEnum(key, value, &instance->config.logo.type, - "auto", FF_LOGO_TYPE_AUTO, - "builtin", FF_LOGO_TYPE_BUILTIN, - "file", FF_LOGO_TYPE_FILE, - "raw", FF_LOGO_TYPE_RAW, - "sixel", FF_LOGO_TYPE_SIXEL, - "kitty", FF_LOGO_TYPE_KITTY, - "chafa", FF_LOGO_TYPE_CHAFA, - NULL - ); - } - else if(strncasecmp(key, "--logo-color-", 13) == 0 && key[13] != '\0' && key[14] == '\0') // matches "--logo-color-*" - { - //Map the number to an array index, so that '1' -> 0, '2' -> 1, etc. - int index = (int)key[13] - 49; - - //Match only --logo-color-[1-9] - if(index < 0 || index >= FASTFETCH_LOGO_MAX_COLORS) + const char* subkey = key + strlen("--logo"); + if(strcasecmp(subkey, "-type") == 0) { - fprintf(stderr, "Error: invalid --color-[1-9] index: %c\n", key[13]); - exit(472); + optionParseEnum(key, value, &instance->config.logo.type, + "auto", FF_LOGO_TYPE_AUTO, + "builtin", FF_LOGO_TYPE_BUILTIN, + "file", FF_LOGO_TYPE_FILE, + "file-raw", FF_LOGO_TYPE_FILE_RAW, + "data", FF_LOGO_TYPE_DATA, + "data-raw", FF_LOGO_TYPE_DATA_RAW, + "sixel", FF_LOGO_TYPE_IMAGE_SIXEL, + "kitty", FF_LOGO_TYPE_IMAGE_KITTY, + "chafa", FF_LOGO_TYPE_IMAGE_CHAFA, + NULL + ); } + else if(startsWith(subkey, "-color-") && key[13] != '\0' && key[14] == '\0') // matches "--logo-color-*" + { + //Map the number to an array index, so that '1' -> 0, '2' -> 1, etc. + int index = (int)key[13] - 49; - optionParseColor(key, value, &instance->config.logo.colors[index]); - } - else if(strcasecmp(key, "--logo-width") == 0) - instance->config.logo.width = optionParseUInt32(key, value); - else if(strcasecmp(key, "--logo-height") == 0) - instance->config.logo.height = optionParseUInt32(key, value); - else if(strcasecmp(key, "--logo-padding") == 0) - { - uint32_t padding = optionParseUInt32(key, value); - instance->config.logo.paddingLeft = padding; - instance->config.logo.paddingRight = padding; - } - else if(strcasecmp(key, "--logo-padding-left") == 0) - instance->config.logo.paddingLeft = optionParseUInt32(key, value); - else if(strcasecmp(key, "--logo-padding-right") == 0) - instance->config.logo.paddingRight = optionParseUInt32(key, value); - else if(strcasecmp(key, "--logo-print-remaining") == 0) - instance->config.logo.printRemaining = optionParseBoolean(value); - else if(strcasecmp(key, "--sixel") == 0) - { - optionParseString(key, value, &instance->config.logo.source); - instance->config.logo.type = FF_LOGO_TYPE_SIXEL; - } - else if(strcasecmp(key, "--kitty") == 0) - { - optionParseString(key, value, &instance->config.logo.source); - instance->config.logo.type = FF_LOGO_TYPE_KITTY; + //Match only --logo-color-[1-9] + if(index < 0 || index >= FASTFETCH_LOGO_MAX_COLORS) + { + fprintf(stderr, "Error: invalid --color-[1-9] index: %c\n", key[13]); + exit(472); + } + + optionParseColor(key, value, &instance->config.logo.colors[index]); + } + else if(strcasecmp(subkey, "-width") == 0) + instance->config.logo.width = optionParseUInt32(key, value); + else if(strcasecmp(subkey, "-height") == 0) + instance->config.logo.height = optionParseUInt32(key, value); + else if(strcasecmp(subkey, "-padding") == 0) + { + uint32_t padding = optionParseUInt32(key, value); + instance->config.logo.paddingLeft = padding; + instance->config.logo.paddingRight = padding; + } + else if(strcasecmp(subkey, "-padding-left") == 0) + instance->config.logo.paddingLeft = optionParseUInt32(key, value); + else if(strcasecmp(subkey, "-padding-right") == 0) + instance->config.logo.paddingRight = optionParseUInt32(key, value); + else if(strcasecmp(subkey, "-print-remaining") == 0) + instance->config.logo.printRemaining = optionParseBoolean(value); + else if(strcasecmp(subkey, "-preserve-aspect-radio") == 0) + instance->config.logo.preserveAspectRadio = optionParseBoolean(value); + else + goto error; } else if(strcasecmp(key, "--file") == 0) { optionParseString(key, value, &instance->config.logo.source); instance->config.logo.type = FF_LOGO_TYPE_FILE; } - else if(strcasecmp(key, "--raw") == 0) + else if(strcasecmp(key, "--file-raw") == 0) { optionParseString(key, value, &instance->config.logo.source); - instance->config.logo.type = FF_LOGO_TYPE_RAW; + instance->config.logo.type = FF_LOGO_TYPE_FILE_RAW; + } + else if(strcasecmp(key, "--data") == 0) + { + optionParseString(key, value, &instance->config.logo.source); + instance->config.logo.type = FF_LOGO_TYPE_DATA; + } + else if(strcasecmp(key, "--data-raw") == 0) + { + optionParseString(key, value, &instance->config.logo.source); + instance->config.logo.type = FF_LOGO_TYPE_DATA_RAW; + } + else if(strcasecmp(key, "--sixel") == 0) + { + optionParseString(key, value, &instance->config.logo.source); + instance->config.logo.type = FF_LOGO_TYPE_IMAGE_SIXEL; + } + else if(strcasecmp(key, "--kitty") == 0) + { + optionParseString(key, value, &instance->config.logo.source); + instance->config.logo.type = FF_LOGO_TYPE_IMAGE_KITTY; } else if(strcasecmp(key, "--chafa") == 0) { optionParseString(key, value, &instance->config.logo.source); - instance->config.logo.type = FF_LOGO_TYPE_CHAFA; + instance->config.logo.type = FF_LOGO_TYPE_IMAGE_CHAFA; } + else if(strcasecmp(key, "--iterm") == 0) + { + optionParseString(key, value, &instance->config.logo.source); + instance->config.logo.type = FF_LOGO_TYPE_IMAGE_ITERM; + } + else if(strcasecmp(key, "--chafa-fg-only") == 0) + instance->config.logo.chafaFgOnly = optionParseBoolean(value); + else if(strcasecmp(key, "--chafa-symbols") == 0) + optionParseString(key, value, &instance->config.logo.chafaSymbols); + else if(strcasecmp(key, "--chafa-canvas-mode") == 0) + instance->config.logo.chafaCanvasMode = optionParseUInt32(key, value); + else if(strcasecmp(key, "--chafa-color-space") == 0) + instance->config.logo.chafaColorSpace = optionParseUInt32(key, value); + else if(strcasecmp(key, "--chafa-dither-mode") == 0) + instance->config.logo.chafaDitherMode = optionParseUInt32(key, value); /////////////////// //Display options// @@ -962,287 +1083,109 @@ static void parseOption(FFinstance* instance, FFdata* data, const char* key, con ); } - //////////////////////////////// - //Format + Key + Error options// - //////////////////////////////// + /////////////////////// + //Module args options// + /////////////////////// - else if(strcasecmp(key, "--os-key") == 0) - optionParseString(key, value, &instance->config.os.key); - else if(strcasecmp(key, "--os-format") == 0) - optionParseString(key, value, &instance->config.os.outputFormat); - else if(strcasecmp(key, "--os-error") == 0) - optionParseString(key, value, &instance->config.os.errorFormat); - else if(strcasecmp(key, "--host-key") == 0) - optionParseString(key, value, &instance->config.host.key); - else if(strcasecmp(key, "--host-format") == 0) - optionParseString(key, value, &instance->config.host.outputFormat); - else if(strcasecmp(key, "--host-error") == 0) - optionParseString(key, value, &instance->config.host.errorFormat); - else if(strcasecmp(key, "--kernel-key") == 0) - optionParseString(key, value, &instance->config.kernel.key); - else if(strcasecmp(key, "--kernel-format") == 0) - optionParseString(key, value, &instance->config.kernel.outputFormat); - else if(strcasecmp(key, "--kernel-error") == 0) - optionParseString(key, value, &instance->config.kernel.errorFormat); - else if(strcasecmp(key, "--uptime-key") == 0) - optionParseString(key, value, &instance->config.uptime.key); - else if(strcasecmp(key, "--uptime-format") == 0) - optionParseString(key, value, &instance->config.uptime.outputFormat); - else if(strcasecmp(key, "--uptime-error") == 0) - optionParseString(key, value, &instance->config.uptime.errorFormat); - else if(strcasecmp(key, "--processes-key") == 0) - optionParseString(key, value, &instance->config.processes.key); - else if(strcasecmp(key, "--processes-format") == 0) - optionParseString(key, value, &instance->config.processes.outputFormat); - else if(strcasecmp(key, "--processes-error") == 0) - optionParseString(key, value, &instance->config.processes.errorFormat); - else if(strcasecmp(key, "--packages-key") == 0) - optionParseString(key, value, &instance->config.packages.key); - else if(strcasecmp(key, "--packages-format") == 0) - optionParseString(key, value, &instance->config.packages.outputFormat); - else if(strcasecmp(key, "--packages-error") == 0) - optionParseString(key, value, &instance->config.packages.errorFormat); - else if(strcasecmp(key, "--shell-key") == 0) - optionParseString(key, value, &instance->config.shell.key); - else if(strcasecmp(key, "--shell-format") == 0) - optionParseString(key, value, &instance->config.shell.outputFormat); - else if(strcasecmp(key, "--shell-error") == 0) - optionParseString(key, value, &instance->config.shell.errorFormat); - else if(strcasecmp(key, "--resolution-key") == 0) - optionParseString(key, value, &instance->config.resolution.key); - else if(strcasecmp(key, "--resolution-format") == 0) - optionParseString(key, value, &instance->config.resolution.outputFormat); - else if(strcasecmp(key, "--resolution-error") == 0) - optionParseString(key, value, &instance->config.resolution.errorFormat); - else if(strcasecmp(key, "--de-key") == 0) - optionParseString(key, value, &instance->config.de.key); - else if(strcasecmp(key, "--de-format") == 0) - optionParseString(key, value, &instance->config.de.outputFormat); - else if(strcasecmp(key, "--de-error") == 0) - optionParseString(key, value, &instance->config.de.errorFormat); - else if(strcasecmp(key, "--wm-key") == 0) - optionParseString(key, value, &instance->config.wm.key); - else if(strcasecmp(key, "--wm-format") == 0) - optionParseString(key, value, &instance->config.wm.outputFormat); - else if(strcasecmp(key, "--wm-error") == 0) - optionParseString(key, value, &instance->config.wm.errorFormat); - else if(strcasecmp(key, "--wm-theme-key") == 0) - optionParseString(key, value, &instance->config.wmTheme.key); - else if(strcasecmp(key, "--wm-theme-format") == 0) - optionParseString(key, value, &instance->config.wmTheme.outputFormat); - else if(strcasecmp(key, "--wm-theme-error") == 0) - optionParseString(key, value, &instance->config.wmTheme.errorFormat); - else if(strcasecmp(key, "--theme-key") == 0) - optionParseString(key, value, &instance->config.theme.key); - else if(strcasecmp(key, "--theme-format") == 0) - optionParseString(key, value, &instance->config.theme.outputFormat); - else if(strcasecmp(key, "--theme-error") == 0) - optionParseString(key, value, &instance->config.theme.errorFormat); - else if(strcasecmp(key, "--icons-key") == 0) - optionParseString(key, value, &instance->config.icons.key); - else if(strcasecmp(key, "--icons-format") == 0) - optionParseString(key, value, &instance->config.icons.outputFormat); - else if(strcasecmp(key, "--icons-error") == 0) - optionParseString(key, value, &instance->config.icons.errorFormat); - else if(strcasecmp(key, "--font-key") == 0) - optionParseString(key, value, &instance->config.font.key); - else if(strcasecmp(key, "--font-format") == 0) - optionParseString(key, value, &instance->config.font.outputFormat); - else if(strcasecmp(key, "--font-error") == 0) - optionParseString(key, value, &instance->config.font.errorFormat); - else if(strcasecmp(key, "--cursor-key") == 0) - optionParseString(key, value, &instance->config.cursor.key); - else if(strcasecmp(key, "--cursor-format") == 0) - optionParseString(key, value, &instance->config.cursor.outputFormat); - else if(strcasecmp(key, "--cursor-error") == 0) - optionParseString(key, value, &instance->config.cursor.errorFormat); - else if(strcasecmp(key, "--terminal-key") == 0) - optionParseString(key, value, &instance->config.terminal.key); - else if(strcasecmp(key, "--terminal-format") == 0) - optionParseString(key, value, &instance->config.terminal.outputFormat); - else if(strcasecmp(key, "--terminal-error") == 0) - optionParseString(key, value, &instance->config.terminal.errorFormat); - else if(strcasecmp(key, "--terminal-font-key") == 0) - optionParseString(key, value, &instance->config.terminalFont.key); - else if(strcasecmp(key, "--terminal-font-format") == 0) - optionParseString(key, value, &instance->config.terminalFont.outputFormat); - else if(strcasecmp(key, "--terminal-font-error") == 0) - optionParseString(key, value, &instance->config.terminalFont.errorFormat); - else if(strcasecmp(key, "--cpu-key") == 0) - optionParseString(key, value, &instance->config.cpu.key); - else if(strcasecmp(key, "--cpu-format") == 0) - optionParseString(key, value, &instance->config.cpu.outputFormat); - else if(strcasecmp(key, "--cpu-error") == 0) - optionParseString(key, value, &instance->config.cpu.errorFormat); - else if(strcasecmp(key, "--cpu-usage-key") == 0) - optionParseString(key, value, &instance->config.cpuUsage.key); - else if(strcasecmp(key, "--cpu-usage-format") == 0) - optionParseString(key, value, &instance->config.cpuUsage.outputFormat); - else if(strcasecmp(key, "--cpu-usage-error") == 0) - optionParseString(key, value, &instance->config.cpuUsage.errorFormat); - else if(strcasecmp(key, "--gpu-key") == 0) - optionParseString(key, value, &instance->config.gpu.key); - else if(strcasecmp(key, "--gpu-format") == 0) - optionParseString(key, value, &instance->config.gpu.outputFormat); - else if(strcasecmp(key, "--gpu-error") == 0) - optionParseString(key, value, &instance->config.gpu.errorFormat); - else if(strcasecmp(key, "--memory-key") == 0) - optionParseString(key, value, &instance->config.memory.key); - else if(strcasecmp(key, "--memory-format") == 0) - optionParseString(key, value, &instance->config.memory.outputFormat); - else if(strcasecmp(key, "--memory-error") == 0) - optionParseString(key, value, &instance->config.memory.errorFormat); - else if(strcasecmp(key, "--swap-key") == 0) - optionParseString(key, value, &instance->config.swap.key); - else if(strcasecmp(key, "--swap-format") == 0) - optionParseString(key, value, &instance->config.swap.outputFormat); - else if(strcasecmp(key, "--swap-error") == 0) - optionParseString(key, value, &instance->config.swap.errorFormat); - else if(strcasecmp(key, "--disk-key") == 0) - optionParseString(key, value, &instance->config.disk.key); - else if(strcasecmp(key, "--disk-format") == 0) - optionParseString(key, value, &instance->config.disk.outputFormat); - else if(strcasecmp(key, "--disk-error") == 0) - optionParseString(key, value, &instance->config.disk.errorFormat); - else if(strcasecmp(key, "--battery-key") == 0) - optionParseString(key, value, &instance->config.battery.key); - else if(strcasecmp(key, "--battery-format") == 0) - optionParseString(key, value, &instance->config.battery.outputFormat); - else if(strcasecmp(key, "--battery-error") == 0) - optionParseString(key, value, &instance->config.battery.errorFormat); - else if(strcasecmp(key, "--poweradapter-key") == 0) - optionParseString(key, value, &instance->config.powerAdapter.key); - else if(strcasecmp(key, "--poweradapter-format") == 0) - optionParseString(key, value, &instance->config.powerAdapter.outputFormat); - else if(strcasecmp(key, "--poweradapter-error") == 0) - optionParseString(key, value, &instance->config.powerAdapter.errorFormat); - else if(strcasecmp(key, "--locale-key") == 0) - optionParseString(key, value, &instance->config.locale.key); - else if(strcasecmp(key, "--locale-format") == 0) - optionParseString(key, value, &instance->config.locale.outputFormat); - else if(strcasecmp(key, "--locale-error") == 0) - optionParseString(key, value, &instance->config.locale.errorFormat); - else if(strcasecmp(key, "--local-ip-key") == 0) - optionParseString(key, value, &instance->config.localIP.key); - else if(strcasecmp(key, "--local-ip-format") == 0) - optionParseString(key, value, &instance->config.localIP.outputFormat); - else if(strcasecmp(key, "--local-ip-error") == 0) - optionParseString(key, value, &instance->config.localIP.errorFormat); - else if(strcasecmp(key, "--public-ip-key") == 0) - optionParseString(key, value, &instance->config.publicIP.key); - else if(strcasecmp(key, "--public-ip-format") == 0) - optionParseString(key, value, &instance->config.publicIP.outputFormat); - else if(strcasecmp(key, "--public-ip-error") == 0) - optionParseString(key, value, &instance->config.publicIP.errorFormat); - else if(strcasecmp(key, "--weather-key") == 0) - optionParseString(key, value, &instance->config.weather.key); - else if(strcasecmp(key, "--weather-format") == 0) - optionParseString(key, value, &instance->config.weather.outputFormat); - else if(strcasecmp(key, "--weather-error") == 0) - optionParseString(key, value, &instance->config.weather.errorFormat); - else if(strcasecmp(key, "--player-key") == 0) - optionParseString(key, value, &instance->config.player.key); - else if(strcasecmp(key, "--player-format") == 0) - optionParseString(key, value, &instance->config.player.outputFormat); - else if(strcasecmp(key, "--player-error") == 0) - optionParseString(key, value, &instance->config.player.errorFormat); - else if(strcasecmp(key, "--song-key") == 0 || strcasecmp(key, "--media-key") == 0) - optionParseString(key, value, &instance->config.song.key); - else if(strcasecmp(key, "--song-format") == 0 || strcasecmp(key, "--media-format") == 0) - optionParseString(key, value, &instance->config.song.outputFormat); - else if(strcasecmp(key, "--song-error") == 0 || strcasecmp(key, "--media-error") == 0) - optionParseString(key, value, &instance->config.song.errorFormat); - else if(strcasecmp(key, "--datetime-key") == 0) - optionParseString(key, value, &instance->config.dateTime.key); - else if(strcasecmp(key, "--datetime-format") == 0) - optionParseString(key, value, &instance->config.dateTime.outputFormat); - else if(strcasecmp(key, "--datetime-error") == 0) - optionParseString(key, value, &instance->config.dateTime.errorFormat); - else if(strcasecmp(key, "--date-key") == 0) - optionParseString(key, value, &instance->config.date.key); - else if(strcasecmp(key, "--date-format") == 0) - optionParseString(key, value, &instance->config.date.outputFormat); - else if(strcasecmp(key, "--date-error") == 0) - optionParseString(key, value, &instance->config.date.errorFormat); - else if(strcasecmp(key, "--time-key") == 0) - optionParseString(key, value, &instance->config.time.key); - else if(strcasecmp(key, "--time-format") == 0) - optionParseString(key, value, &instance->config.time.outputFormat); - else if(strcasecmp(key, "--time-error") == 0) - optionParseString(key, value, &instance->config.time.errorFormat); - else if(strcasecmp(key, "--vulkan-key") == 0) - optionParseString(key, value, &instance->config.vulkan.key); - else if(strcasecmp(key, "--vulkan-format") == 0) - optionParseString(key, value, &instance->config.vulkan.outputFormat); - else if(strcasecmp(key, "--vulkan-error") == 0) - optionParseString(key, value, &instance->config.vulkan.errorFormat); - else if(strcasecmp(key, "--opengl-key") == 0) - optionParseString(key, value, &instance->config.openGL.key); - else if(strcasecmp(key, "--opengl-format") == 0) - optionParseString(key, value, &instance->config.openGL.outputFormat); - else if(strcasecmp(key, "--opengl-error") == 0) - optionParseString(key, value, &instance->config.openGL.errorFormat); - else if(strcasecmp(key, "--opencl-key") == 0) - optionParseString(key, value, &instance->config.openCL.key); - else if(strcasecmp(key, "--opencl-format") == 0) - optionParseString(key, value, &instance->config.openCL.outputFormat); - else if(strcasecmp(key, "--opencl-error") == 0) - optionParseString(key, value, &instance->config.openCL.errorFormat); - else if(strcasecmp(key, "--users-key") == 0) - optionParseString(key, value, &instance->config.users.key); - else if(strcasecmp(key, "--users-format") == 0) - optionParseString(key, value, &instance->config.users.outputFormat); - else if(strcasecmp(key, "--users-error") == 0) - optionParseString(key, value, &instance->config.users.errorFormat); + else if(optionParseModuleArgs(key, value, "os", &instance->config.os)) {} + else if(optionParseModuleArgs(key, value, "host", &instance->config.host)) {} + else if(optionParseModuleArgs(key, value, "bios", &instance->config.bios)) {} + else if(optionParseModuleArgs(key, value, "board", &instance->config.board)) {} + else if(optionParseModuleArgs(key, value, "chassis", &instance->config.chassis)) {} + else if(optionParseModuleArgs(key, value, "kernel", &instance->config.kernel)) {} + else if(optionParseModuleArgs(key, value, "uptime", &instance->config.uptime)) {} + else if(optionParseModuleArgs(key, value, "processes", &instance->config.processes)) {} + else if(optionParseModuleArgs(key, value, "packages", &instance->config.packages)) {} + else if(optionParseModuleArgs(key, value, "shell", &instance->config.shell)) {} + else if(optionParseModuleArgs(key, value, "resolution", &instance->config.resolution)) {} + else if(optionParseModuleArgs(key, value, "de", &instance->config.de)) {} + else if(optionParseModuleArgs(key, value, "wifi", &instance->config.wifi)) {} + else if(optionParseModuleArgs(key, value, "wm", &instance->config.wm)) {} + else if(optionParseModuleArgs(key, value, "wm-theme", &instance->config.wmTheme)) {} + else if(optionParseModuleArgs(key, value, "theme", &instance->config.theme)) {} + else if(optionParseModuleArgs(key, value, "icons", &instance->config.icons)) {} + else if(optionParseModuleArgs(key, value, "font", &instance->config.font)) {} + else if(optionParseModuleArgs(key, value, "cursor", &instance->config.cursor)) {} + else if(optionParseModuleArgs(key, value, "terminal", &instance->config.terminal)) {} + else if(optionParseModuleArgs(key, value, "terminal-font", &instance->config.terminalFont)) {} + else if(optionParseModuleArgs(key, value, "cpu", &instance->config.cpu)) {} + else if(optionParseModuleArgs(key, value, "cpu-usage", &instance->config.cpuUsage)) {} + else if(optionParseModuleArgs(key, value, "gpu", &instance->config.gpu)) {} + else if(optionParseModuleArgs(key, value, "memory", &instance->config.memory)) {} + else if(optionParseModuleArgs(key, value, "swap", &instance->config.swap)) {} + else if(optionParseModuleArgs(key, value, "disk", &instance->config.disk)) {} + else if(optionParseModuleArgs(key, value, "battery", &instance->config.battery)) {} + else if(optionParseModuleArgs(key, value, "poweradapter", &instance->config.powerAdapter)) {} + else if(optionParseModuleArgs(key, value, "locale", &instance->config.locale)) {} + else if(optionParseModuleArgs(key, value, "local-ip", &instance->config.localIP)) {} + else if(optionParseModuleArgs(key, value, "public-ip", &instance->config.publicIP)) {} + else if(optionParseModuleArgs(key, value, "weather", &instance->config.weather)) {} + else if(optionParseModuleArgs(key, value, "player", &instance->config.player)) {} + else if(optionParseModuleArgs(key, value, "song", &instance->config.song)) {} + else if(optionParseModuleArgs(key, value, "datetime", &instance->config.dateTime)) {} + else if(optionParseModuleArgs(key, value, "date", &instance->config.date)) {} + else if(optionParseModuleArgs(key, value, "time", &instance->config.time)) {} + else if(optionParseModuleArgs(key, value, "vulkan", &instance->config.vulkan)) {} + else if(optionParseModuleArgs(key, value, "opengl", &instance->config.openGL)) {} + else if(optionParseModuleArgs(key, value, "opencl", &instance->config.openCL)) {} + else if(optionParseModuleArgs(key, value, "users", &instance->config.users)) {} /////////////////// //Library options// /////////////////// - else if(strcasecmp(key, "--lib-PCI") == 0) - optionParseString(key, value, &instance->config.libPCI); - else if(strcasecmp(key, "--lib-vulkan") == 0) - optionParseString(key, value, &instance->config.libVulkan); - else if(strcasecmp(key, "--lib-freetype") == 0) - optionParseString(key, value, &instance->config.libfreetype); - else if(strcasecmp(key, "--lib-wayland") == 0) - optionParseString(key, value, &instance->config.libWayland); - else if(strcasecmp(key, "--lib-xcb-randr") == 0) - optionParseString(key, value, &instance->config.libXcbRandr); - else if(strcasecmp(key, "--lib-xcb") == 0) - optionParseString(key, value, &instance->config.libXcb); - else if(strcasecmp(key, "--lib-Xrandr") == 0) - optionParseString(key, value, &instance->config.libXrandr); - else if(strcasecmp(key, "--lib-X11") == 0) - optionParseString(key, value, &instance->config.libX11); - else if(strcasecmp(key, "--lib-gio") == 0) - optionParseString(key, value, &instance->config.libGIO); - else if(strcasecmp(key, "--lib-DConf") == 0) - optionParseString(key, value, &instance->config.libDConf); - else if(strcasecmp(key, "--lib-dbus") == 0) - optionParseString(key, value, &instance->config.libDBus); - else if(strcasecmp(key, "--lib-XFConf") == 0) - optionParseString(key, value, &instance->config.libXFConf); - else if(strcasecmp(key, "--lib-sqlite") == 0 || strcasecmp(key, "--lib-sqlite3") == 0) - optionParseString(key, value, &instance->config.libSQLite3); - else if(strcasecmp(key, "--lib-rpm") == 0) - optionParseString(key, value, &instance->config.librpm); - else if(strcasecmp(key, "--lib-imagemagick") == 0) - optionParseString(key, value, &instance->config.libImageMagick); - else if(strcasecmp(key, "--lib-z") == 0) - optionParseString(key, value, &instance->config.libZ); - else if(strcasecmp(key, "--lib-chafa") == 0) - optionParseString(key, value, &instance->config.libChafa); - else if(strcasecmp(key, "--lib-egl") == 0) - optionParseString(key, value, &instance->config.libEGL); - else if(strcasecmp(key, "--lib-glx") == 0) - optionParseString(key, value, &instance->config.libGLX); - else if(strcasecmp(key, "--lib-osmesa") == 0) - optionParseString(key, value, &instance->config.libOSMesa); - else if(strcasecmp(key, "--lib-opencl") == 0) - optionParseString(key, value, &instance->config.libOpenCL); - else if(strcasecmp(key, "--lib-cjson") == 0) - optionParseString(key, value, &instance->config.libcJSON); + else if(startsWith(key, "--lib")) + { + const char* subkey = key + strlen("--lib"); + if(strcasecmp(subkey, "-PCI") == 0) + optionParseString(key, value, &instance->config.libPCI); + else if(strcasecmp(subkey, "-vulkan") == 0) + optionParseString(key, value, &instance->config.libVulkan); + else if(strcasecmp(subkey, "-freetype") == 0) + optionParseString(key, value, &instance->config.libfreetype); + else if(strcasecmp(subkey, "-wayland") == 0) + optionParseString(key, value, &instance->config.libWayland); + else if(strcasecmp(subkey, "-xcb-randr") == 0) + optionParseString(key, value, &instance->config.libXcbRandr); + else if(strcasecmp(subkey, "-xcb") == 0) + optionParseString(key, value, &instance->config.libXcb); + else if(strcasecmp(subkey, "-Xrandr") == 0) + optionParseString(key, value, &instance->config.libXrandr); + else if(strcasecmp(subkey, "-X11") == 0) + optionParseString(key, value, &instance->config.libX11); + else if(strcasecmp(subkey, "-gio") == 0) + optionParseString(key, value, &instance->config.libGIO); + else if(strcasecmp(subkey, "-DConf") == 0) + optionParseString(key, value, &instance->config.libDConf); + else if(strcasecmp(subkey, "-dbus") == 0) + optionParseString(key, value, &instance->config.libDBus); + else if(strcasecmp(subkey, "-XFConf") == 0) + optionParseString(key, value, &instance->config.libXFConf); + else if(strcasecmp(subkey, "-sqlite") == 0 || strcasecmp(subkey, "-sqlite3") == 0) + optionParseString(key, value, &instance->config.libSQLite3); + else if(strcasecmp(subkey, "-rpm") == 0) + optionParseString(key, value, &instance->config.librpm); + else if(strcasecmp(subkey, "-imagemagick") == 0) + optionParseString(key, value, &instance->config.libImageMagick); + else if(strcasecmp(subkey, "-z") == 0) + optionParseString(key, value, &instance->config.libZ); + else if(strcasecmp(subkey, "-chafa") == 0) + optionParseString(key, value, &instance->config.libChafa); + else if(strcasecmp(subkey, "-egl") == 0) + optionParseString(key, value, &instance->config.libEGL); + else if(strcasecmp(subkey, "-glx") == 0) + optionParseString(key, value, &instance->config.libGLX); + else if(strcasecmp(subkey, "-osmesa") == 0) + optionParseString(key, value, &instance->config.libOSMesa); + else if(strcasecmp(subkey, "-opencl") == 0) + optionParseString(key, value, &instance->config.libOpenCL); + else if(strcasecmp(subkey, "-cjson") == 0) + optionParseString(key, value, &instance->config.libcJSON); + else if(strcasecmp(subkey, "-wlanapi") == 0) + optionParseString(key, value, &instance->config.libwlanapi); + else + goto error; + } ////////////////// //Module options// @@ -1258,8 +1201,10 @@ static void parseOption(FFinstance* instance, FFdata* data, const char* key, con instance->config.titleFQDN = optionParseBoolean(value); else if(strcasecmp(key, "--disk-folders") == 0) optionParseString(key, value, &instance->config.diskFolders); - else if(strcasecmp(key, "--disk-removable") == 0) - instance->config.diskRemovable = optionParseBoolean(value); + else if(strcasecmp(key, "--disk-show-removable") == 0) + instance->config.diskShowRemovable = optionParseBoolean(value); + else if(strcasecmp(key, "--disk-show-hidden") == 0) + instance->config.diskShowHidden = optionParseBoolean(value); else if(strcasecmp(key, "--battery-dir") == 0) optionParseString(key, value, &instance->config.batteryDir); else if(strcasecmp(key, "--separator-string") == 0) @@ -1294,6 +1239,8 @@ static void parseOption(FFinstance* instance, FFdata* data, const char* key, con NULL ); } + else if(strcasecmp(key, "--percent-type") == 0) + instance->config.percentType = optionParseUInt32(key, value); ////////////////// //Unknown option// @@ -1301,6 +1248,7 @@ static void parseOption(FFinstance* instance, FFdata* data, const char* key, con else { +error: fprintf(stderr, "Error: unknown option: %s\n", key); exit(400); } @@ -1367,6 +1315,12 @@ static void parseStructureCommand(FFinstance* instance, FFdata* data, const char ffPrintOS(instance); else if(strcasecmp(line, "host") == 0) ffPrintHost(instance); + else if(strcasecmp(line, "bios") == 0) + ffPrintBios(instance); + else if(strcasecmp(line, "board") == 0) + ffPrintBoard(instance); + else if(strcasecmp(line, "chassis") == 0) + ffPrintChassis(instance); else if(strcasecmp(line, "kernel") == 0) ffPrintKernel(instance); else if(strcasecmp(line, "uptime") == 0) @@ -1419,6 +1373,8 @@ static void parseStructureCommand(FFinstance* instance, FFdata* data, const char ffPrintLocalIp(instance); else if(strcasecmp(line, "publicip") == 0) ffPrintPublicIp(instance); + else if(strcasecmp(line, "wifi") == 0) + ffPrintWifi(instance); else if(strcasecmp(line, "weather") == 0) ffPrintWeather(instance); else if(strcasecmp(line, "player") == 0) @@ -1482,8 +1438,26 @@ int main(int argc, const char** argv) uint32_t colonIndex = ffStrbufNextIndexC(&data.structure, startIndex, ':'); data.structure.chars[colonIndex] = '\0'; + uint64_t ms = 0; + if(__builtin_expect(instance.config.stat, false)) + ms = ffTimeGetTick(); + parseStructureCommand(&instance, &data, data.structure.chars + startIndex); + if(__builtin_expect(instance.config.stat, false)) + { + char str[32]; + int len = snprintf(str, sizeof str, "%" PRIu64 "ms", ffTimeGetTick() - ms); + if(instance.config.pipe) + puts(str); + else + printf("\033[s\033[1A\033[9999999C\033[%dD%s\033[u", len, str); // Save; Up 1; Right 9999999; Left ; Print ; Load + } + + #if defined(_WIN32) && defined(FF_ENABLE_BUFFER) + fflush(stdout); + #endif + startIndex = colonIndex + 1; } diff --git a/src/fastfetch.h b/src/fastfetch.h index f466d59e5..757e5f083 100644 --- a/src/fastfetch.h +++ b/src/fastfetch.h @@ -8,11 +8,12 @@ #include #include -#include -#include - -#if FF_HAVE_SYSINFO_H - #include +#ifndef _WIN32 + #include + #include +#else + #include "util/windows/pwd.h" + #include "util/windows/utsname.h" #endif #include "util/FFstrbuf.h" @@ -20,22 +21,22 @@ static inline void ffUnused(int dummy, ...) { (void) dummy; } #define FF_UNUSED(...) ffUnused(0, __VA_ARGS__); - -#define FASTFETCH_TEXT_MODIFIER_BOLT "\033[1m" -#define FASTFETCH_TEXT_MODIFIER_ERROR "\033[1;31m" -#define FASTFETCH_TEXT_MODIFIER_RESET "\033[0m" +#define FF_UNUSED_PARAM __attribute__ ((__unused__)) #define FASTFETCH_LOGO_MAX_COLORS 9 //two digits would make parsing much more complicated (index 1 - 9) typedef enum FFLogoType { - FF_LOGO_TYPE_AUTO, //If something is given, first try builtin, then file. Otherwise detect logo - FF_LOGO_TYPE_BUILTIN, //Builtin ascii art. - FF_LOGO_TYPE_FILE, //Raw text file, printed as is. - FF_LOGO_TYPE_RAW, //Raw text file, printed with color codes replacement. - FF_LOGO_TYPE_SIXEL, //Image file, printed as sixel codes. - FF_LOGO_TYPE_KITTY, //Image file, printed as kitty graphics protocol - FF_LOGO_TYPE_CHAFA //Image file, printed as ascii art using libchafa + FF_LOGO_TYPE_AUTO, //if something is given, first try builtin, then file. Otherwise detect logo + FF_LOGO_TYPE_BUILTIN, //builtin ascii art + FF_LOGO_TYPE_FILE, //text file, printed with color code replacement + FF_LOGO_TYPE_FILE_RAW, //text file, printed as is + FF_LOGO_TYPE_DATA, //text data, printed with color code replacement + FF_LOGO_TYPE_DATA_RAW, //text data, printed as is + FF_LOGO_TYPE_IMAGE_SIXEL, //image file, printed as sixel codes. + FF_LOGO_TYPE_IMAGE_KITTY, //image file, printed as kitty graphics protocol + FF_LOGO_TYPE_IMAGE_ITERM, //image file, printed as iterm graphics protocol + FF_LOGO_TYPE_IMAGE_CHAFA, //image file, printed as ascii art using libchafa } FFLogoType; typedef enum FFBinaryPrefixType @@ -72,6 +73,13 @@ typedef struct FFconfig uint32_t paddingLeft; uint32_t paddingRight; bool printRemaining; + bool preserveAspectRadio; + + bool chafaFgOnly; + FFstrbuf chafaSymbols; + uint32_t chafaCanvasMode; + uint32_t chafaColorSpace; + uint32_t chafaDitherMode; } logo; //If one of those is empty, ffLogoPrint will set them @@ -82,7 +90,6 @@ typedef struct FFconfig bool showErrors; bool recache; - bool cacheSave; bool allowSlowOperations; bool disableLinewrap; bool hideCursor; @@ -91,9 +98,13 @@ typedef struct FFconfig FFGLType glType; bool pipe; //disables logo and all escape sequences bool multithreading; + bool stat; FFModuleArgs os; FFModuleArgs host; + FFModuleArgs bios; + FFModuleArgs chassis; + FFModuleArgs board; FFModuleArgs kernel; FFModuleArgs uptime; FFModuleArgs processes; @@ -101,6 +112,7 @@ typedef struct FFconfig FFModuleArgs shell; FFModuleArgs resolution; FFModuleArgs de; + FFModuleArgs wifi; FFModuleArgs wm; FFModuleArgs wmTheme; FFModuleArgs theme; @@ -153,6 +165,7 @@ typedef struct FFconfig FFstrbuf libOpenCL; FFstrbuf libcJSON; FFstrbuf libfreetype; + FFstrbuf libwlanapi; bool cpuTemp; bool gpuTemp; @@ -161,7 +174,8 @@ typedef struct FFconfig bool titleFQDN; FFstrbuf diskFolders; - bool diskRemovable; + bool diskShowRemovable; + bool diskShowHidden; FFstrbuf batteryDir; @@ -181,6 +195,8 @@ typedef struct FFconfig FFstrbuf osFile; FFstrbuf playerName; + + uint32_t percentType; } FFconfig; typedef struct FFstate @@ -192,12 +208,7 @@ typedef struct FFstate struct passwd* passwd; struct utsname utsname; - #if FF_HAVE_SYSINFO_H - struct sysinfo sysinfo; - #endif - FFlist configDirs; - FFstrbuf cacheDir; } FFstate; typedef struct FFinstance @@ -249,6 +260,9 @@ void ffPrintTitle(FFinstance* instance); void ffPrintSeparator(FFinstance* instance); void ffPrintOS(FFinstance* instance); void ffPrintHost(FFinstance* instance); +void ffPrintBios(FFinstance* instance); +void ffPrintBoard(FFinstance* instance); +void ffPrintChassis(FFinstance* instance); void ffPrintKernel(FFinstance* instance); void ffPrintUptime(FFinstance* instance); void ffPrintProcesses(FFinstance* instance); @@ -281,6 +295,7 @@ void ffPrintTime(FFinstance* instance); void ffPrintLocalIp(FFinstance* instance); void ffPrintPublicIp(FFinstance* instance); void ffPrintWeather(FFinstance* instance); +void ffPrintWifi(FFinstance* instance); void ffPrintColors(FFinstance* instance); void ffPrintVulkan(FFinstance* instance); void ffPrintOpenGL(FFinstance* instance); diff --git a/src/fastfetch_config.h.in b/src/fastfetch_config.h.in index 40677fa6b..0fcf3f2d1 100644 --- a/src/fastfetch_config.h.in +++ b/src/fastfetch_config.h.in @@ -7,6 +7,10 @@ #define FASTFETCH_PROJECT_VERSION_MINOR @PROJECT_VERSION_MINOR@ #define FASTFETCH_PROJECT_VERSION_PATCH @PROJECT_VERSION_PATCH@ #define FASTFETCH_PROJECT_VERSION_TWEAK "@PROJECT_VERSION_TWEAK@" +#define FASTFETCH_PROJECT_VERSION_TWEAK_NUM @PROJECT_VERSION_TWEAK_NUM@ +#define FASTFETCH_PROJECT_HOMEPAGE_URL "@PROJECT_HOMEPAGE_URL@" +#define FASTFETCH_PROJECT_DESCRIPTION "@PROJECT_DESCRIPTION@" +#define FASTFETCH_PROJECT_LICENSE "@PROJECT_LICENSE@" #define FASTFETCH_TARGET_DIR_ROOT "@TARGET_DIR_ROOT@" #define FASTFETCH_TARGET_DIR_USR "@TARGET_DIR_USR@" @@ -16,7 +20,7 @@ #define FASTFETCH_TARGET_DIR_INSTALL_SYSCONF "@CMAKE_INSTALL_SYSCONFDIR@" #define FASTFETCH_DATATEXT_STRUCTURE "@DATATEXT_STRUCTURE@" -#define FASTFETCH_DATATEXT_CONFIG_SYSTEM "@DATATEXT_CONFIG_SYSTEM@" //Requires FASTFETCH_PROJECT_VERSION to be set +#define FASTFETCH_DATATEXT_CONFIG_SYSTEM "@DATATEXT_CONFIG_SYSTEM@" #define FASTFETCH_DATATEXT_CONFIG_USER "@DATATEXT_CONFIG_USER@" //Requires FASTFETCH_PROJECT_VERSION and FASTFETCH_DATATEXT_STRUCTURE to be set #define FASTFETCH_DATATEXT_MODULES "@DATATEXT_MODULES@" #define FASTFETCH_DATATEXT_HELP "@DATATEXT_HELP@" diff --git a/src/flashfetch.c b/src/flashfetch.c index e20b2aa1b..769cdfc5d 100644 --- a/src/flashfetch.c +++ b/src/flashfetch.c @@ -22,6 +22,9 @@ int main(int argc, char** argv) ffPrintSeparator(&instance); ffPrintOS(&instance); ffPrintHost(&instance); + //ffPrintBios(&instance); + //ffPrintBoard(&instance); + //ffPrintChassis(&instance); ffPrintKernel(&instance); ffPrintUptime(&instance); //ffPrintProcesses(&instance); @@ -48,6 +51,7 @@ int main(int argc, char** argv) //ffPrintSong(&instance); //ffPrintLocalIp(&instance); //ffPrintPublicIp(&instance); + //ffPrintWifi(&instance); //ffPrintCPUUsage(&instance); ffPrintLocale(&instance); //ffPrintDateTime(&instance); diff --git a/src/logo/builtin.c b/src/logo/builtin.c index 991115c0b..d13b7e4f8 100644 --- a/src/logo/builtin.c +++ b/src/logo/builtin.c @@ -87,7 +87,7 @@ static const FFlogo* getLogoAlpine() " +dddddddddddddddddddddddddddddd+\n" " `sdddddddddddddddddddddddddddddddds`\n" " `ydddddddddddd++hdddddddddddddddddddy`\n" - ".hddddddddddd+` `+ddddh:-sdddddddddddh. \n" + ".hddddddddddd+` `+ddddh:-sdddddddddddh.\n" "hdddddddddd+` `+y: .sddddddddddh\n" "ddddddddh+` `//` `.` -sddddddddd\n" "ddddddh+` `/hddh/` `:s- -sddddddd\n" @@ -511,6 +511,40 @@ static const FFlogo* getLogoCentOSSmall() FF_LOGO_RETURN } +static const FFlogo* getLogoCRUX() +{ + FF_LOGO_INIT + FF_LOGO_NAMES("CRUX", "crux") + FF_LOGO_LINES( + " $1odddd\n" + " oddxkkkxxdoo\n" + " ddcoddxxxdoool\n" + " xdclodod olol\n" + " xoc xdd olol\n" + " xdc $2k00$1Okdlol\n" + " xxd$2kOKKKOkd$1ldd\n" + " xdco$2xOkdlo$1dldd\n" + " ddc:cl$2lll$1oooodo\n" + " odxxdd$3xkO000kx$1ooxdo\n" + " oxddx$30NMMMMMMWW0o$1dkkxo\n" + " oooxd$30WMMMMMMMMMW0o$1dxkx\n" + "docldkXW$3MMMMMMMWWN$1Odolco\n" + "xx$2dx$1kxxOKN$3WMMWN$10xdoxo::c\n" + "$2xOkkO$10oo$3odOW$2WW$1XkdodOxc:l\n" + "$2dkkkxkkk$3OKX$2NNNX0Oxx$1xc:cd\n" + " $2odxxdx$3xllo$2dddooxx$1dc:ldo\n" + " $2lodd$1dolccc$2ccox$1xoloo\n" + ) + FF_LOGO_COLORS( + "34", //blue + "35", //magenta + "37" //white + ) + FF_LOGO_COLOR_KEYS("35"); + FF_LOGO_COLOR_TITLE("34"); + FF_LOGO_RETURN +} + static const FFlogo* getLogoCrystalLinux() { FF_LOGO_INIT @@ -699,8 +733,8 @@ static const FFlogo* getLogoEndeavour() " $2`:////$1ssssssssssssssssssssssssssso$3+++.\n" "$2`-////+$1ssssssssssssssssssssssssssso$3++++-\n" " $2`..-+$1oosssssssssssssssssssssssso$3+++++/`\n" - "$3./++++++++++++++++++++++++++++++/:.\n" - "`:::::::::::::::::::::::::------``" + " $3./++++++++++++++++++++++++++++++/:.\n" + " `:::::::::::::::::::::::::------``" ) FF_LOGO_COLORS( "35", //magenta @@ -712,6 +746,39 @@ static const FFlogo* getLogoEndeavour() FF_LOGO_RETURN } +static const FFlogo* getLogoEnso() +{ + FF_LOGO_INIT + FF_LOGO_NAMES("enso", "uqc") + FF_LOGO_LINES( + " .:--==--:. \n" + " :=*#############*+-. \n" + " .+##################*##*: \n" + " .*##########+==-==++*####*##- \n" + " =########=: .-+**#***. \n" + " *#######- ++*#**. \n" + " +######+ -*+#** \n" + " :######* .*+**= \n" + " *######: --#*# \n" + " ####### +++#. \n" + " #######. ++=*. \n" + " *######+ .-+*+ \n" + " :#######- -:*+: \n" + " =#######*. :.*+- \n" + " +########*- :*=- \n" + " =###########+=: =+=: \n" + " .+#############. .-==: \n" + " .=###########= ..:--:. \n" + " .-+######+ \n" + ) + FF_LOGO_COLORS( + "37" //white + ) + FF_LOGO_COLOR_KEYS("37"); //white + FF_LOGO_COLOR_TITLE("37"); //white + FF_LOGO_RETURN +} + static const FFlogo* getLogoFedora() { FF_LOGO_INIT @@ -863,6 +930,25 @@ static const FFlogo* getLogoGaruda() FF_LOGO_RETURN } +static const FFlogo* getLogoGarudaSmall() +{ + FF_LOGO_INIT + FF_LOGO_NAMES("garuda_small", "garudalinux_small", "garuda-linux-small") + FF_LOGO_LINES( + " .----.\n" + " .' , '.\n" + " .' '-----|\n" + "'. -----,\n" + " '.____.'" + ) + FF_LOGO_COLORS( + "31" //red + ) + FF_LOGO_COLOR_KEYS("31"); //red + FF_LOGO_COLOR_TITLE("31"); //red + FF_LOGO_RETURN +} + static const FFlogo* getLogoGentoo() { FF_LOGO_INIT @@ -1277,6 +1363,128 @@ static const FFlogo* getLogoMsys2() FF_LOGO_RETURN } +static const FFlogo* getLogoWindows11() +{ + FF_LOGO_INIT + FF_LOGO_NAMES("Windows 11", "Windows Server 2022") + FF_LOGO_LINES( + "$1\n" + ",,**************/ ///////////////()\n" + "****************/ ///////////////((\n" + "***************// //////////////(((\n" + "**************/// ////////////(((((\n" + "************///// /////////((((((((\n" + "*********//////// /////((((((((((((\n" + "*******////////// ///((((((((((((((\n" + "****///////////// (((((((((((((((((\n" + "\n" + "/////////////(((( (((((((((((((((((\n" + "////////////((((( (((((((((((((((((\n" + "///////////(((((( (((((((((((((((((\n" + "/////////(((((((( (((((((((((((((((\n" + "///////(((((((((( (((((((((((((((((\n" + "////((((((((((((( (((((((((((((((((\n" + "((((((((((((((((( (((((((((((((((((\n" + "((((((((((((((((( (((((((((((((((()" + ) + FF_LOGO_COLORS( + "34" //blue + ) + FF_LOGO_COLOR_KEYS("33"); //yellow + FF_LOGO_COLOR_TITLE("36"); //cyan + FF_LOGO_RETURN +} + +static const FFlogo* getLogoWindows11Small() +{ + FF_LOGO_INIT + FF_LOGO_NAMES("Windows 11_small", "Windows 11-small") + FF_LOGO_LINES( + "$1\n" + "lllllllll lllllllll\n" + "lllllllll lllllllll\n" + "lllllllll lllllllll\n" + "lllllllll lllllllll\n" + "\n" + "lllllllll lllllllll\n" + "lllllllll lllllllll\n" + "lllllllll lllllllll\n" + "lllllllll lllllllll\n" + ) + FF_LOGO_COLORS( + "34" //blue + ) + FF_LOGO_COLOR_KEYS("33"); //yellow + FF_LOGO_COLOR_TITLE("36"); //cyan + FF_LOGO_RETURN +} + +static const FFlogo* getLogoWindows8() +{ + FF_LOGO_INIT + FF_LOGO_NAMES("Windows 8", "Windows 8.1", "Windows 10", "Windows Server 2012", "Windows Server 2012 R2", "Windows Server 2016", "Windows Server 2019") + FF_LOGO_LINES( + "$1 ..,\n" + " ....,,:;+ccllll\n" + " ...,,+:; cllllllllllllllllll\n" + ",cclllllllllll lllllllllllllllllll\n" + "llllllllllllll lllllllllllllllllll\n" + "llllllllllllll lllllllllllllllllll\n" + "llllllllllllll lllllllllllllllllll\n" + "llllllllllllll lllllllllllllllllll\n" + "llllllllllllll lllllllllllllllllll\n" + "\n" + "llllllllllllll lllllllllllllllllll\n" + "llllllllllllll lllllllllllllllllll\n" + "llllllllllllll lllllllllllllllllll\n" + "llllllllllllll lllllllllllllllllll\n" + "llllllllllllll lllllllllllllllllll\n" + "`'ccllllllllll lllllllllllllllllll\n" + " `' \\*:: :ccllllllllllllllll\n" + " ````''*::cll\n" + " ``" + ) + FF_LOGO_COLORS( + "36" //cyan + ) + FF_LOGO_COLOR_KEYS("33"); //yellow + FF_LOGO_COLOR_TITLE("37"); //white + FF_LOGO_RETURN +} + +static const FFlogo* getLogoWindows() +{ + FF_LOGO_INIT + FF_LOGO_NAMES("Windows", "Windows 7", "Windows Server 2008", "Windows Server 2008 R2") + FF_LOGO_LINES( + "$1 ,.=:!!t3Z3z.,\n" + " :tt:::tt333EE3\n" + "$1 Et:::ztt33EEEL$2 @Ee., ..,\n" + "$1 ;tt:::tt333EE7$2 ;EEEEEEttttt33#\n" + "$1 :Et:::zt333EEQ.$2 $EEEEEttttt33QL\n" + "$1 it::::tt333EEF$2 @EEEEEEttttt33F\n" + "$1 ;3=*^```\"*4EEV$2 :EEEEEEttttt33@.\n" + "$3 ,.=::::!t=., $1`$2 @EEEEEEtttz33QF\n" + "$3 ;::::::::zt33)$2 \"4EEEtttji3P*\n" + "$3 :t::::::::tt33.$4:Z3z..$2 ``$4 ,..g.\n" + "$3 i::::::::zt33F$4 AEEEtttt::::ztF\n" + "$3 ;:::::::::t33V$4 ;EEEttttt::::t3\n" + "$3 E::::::::zt33L$4 @EEEtttt::::z3F\n" + "$3{3=*^```\"*4E3)$4 ;EEEtttt:::::tZ`\n" + "$3 `$4 :EEEEtttt::::z7\n" + " \"VEzjt:;;z>*`" + ) + FF_LOGO_COLORS( + "31", //red + "32", //green + "34", //blue + "33" //yellow + ) + FF_LOGO_COLOR_KEYS("34"); //blue + FF_LOGO_COLOR_TITLE("32"); //green + FF_LOGO_RETURN +} + static const FFlogo* getLogoNixOS() { FF_LOGO_INIT @@ -1376,6 +1584,37 @@ static const FFlogo* getLogoNixOsSmall() FF_LOGO_RETURN } +static const FFlogo* getLogoNobara() +{ + FF_LOGO_INIT + FF_LOGO_NAMES("nobara", "nobara-linux"); + FF_LOGO_LINES( + "⢀⣤⣴⣶⣶⣶⣦⣤⡀⠀⣀⣠⣤⣴⣶⣶⣶⣶⣶⣶⣶⣶⣤⣤⣀⡀\n" + "⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣶⣤⡀\n" + "⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣷⣄\n" + "⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣷⣄\n" + "⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣧\n" + "⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⠟⠋⠉⠁⠀⠀⠉⠉⠛⠿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣧\n" + "⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⠟⠁⠀⠀⠀⢀⣀⣀⡀⠀⠀⠀⠈⢻⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⡇\n" + "⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⡏⠀⠀⠀⢠⣾⣿⣿⣿⣿⣷⡄⠀⠀⠀⠻⠿⢿⣿⣿⣿⣿⣿⣿⣿⣿⣿\n" + "⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⠁⠀⠀⠀⣿⣿⣿⣿⣿⣿⣿⡇⠀⠀⠀⠀⠀⣀⣀⣬⣽⣿⣿⣿⣿⣿⣿\n" + "⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⠀⠀⠀⠀⠈⠻⢿⣿⣿⡿⠟⠁⠀⠀⠀⢸⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿\n" + "⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣇⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⢸⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿\n" + "⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣷⣤⣤⣄⣀⡀⠀⠀⠀⠀⠀⠀⠀⠀⢸⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿\n" + "⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣷⣄⠀⠀⠀⠀⠀⠀⢸⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿\n" + "⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣇⠀⠀⠀⠀⠀⢸⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿\n" + "⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⡿⠟⠛⠉⠉⠛⠛⢿⣿⣿⠀⠀⠀⠀⠀⠸⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⡿\n" + "⠘⢿⣿⣿⣿⣿⣿⣿⣿⡿⠋⠀⠀⠀⠀⠀⠀⠀⠀⠈⢿⠀⠀⠀⠀⠀⠀⠙⢿⣿⣿⣿⣿⣿⣿⣿⠟⠁\n" + "⠈⠙⠛⠛⠛⠋⠁⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠉⠛⠛⠛⠛⠉⠁\n" + ) + FF_LOGO_COLORS( + "37" // white + ) + FF_LOGO_COLOR_KEYS("37"); // white + FF_LOGO_COLOR_TITLE("37"); // white + FF_LOGO_RETURN +} + static const FFlogo* getLogoOpenSuse() { FF_LOGO_INIT @@ -1487,6 +1726,45 @@ static const FFlogo* getLogoOpenSuseTumbleweed() FF_LOGO_RETURN } +static const FFlogo* getLogoOpenMandriva() +{ + FF_LOGO_INIT + FF_LOGO_NAMES("openmandriva", "open-mandriva", "open_mandriva") + FF_LOGO_LINES( + " ``````\n" + " `-:/+++++++//:-.`\n" + " .:+++oooo+/:.`` ``\n" + " `:+ooooooo+:. `-:/++++++/:.`\n" + " -+oooooooo:` `-++o+/::::://+o+/-\n" + " `/ooooooooo- -+oo/.` `-/oo+.\n" + " `+ooooooooo. :os/` .+so:\n" + " +sssssssss/ :ss/ `+ss-\n" + " :ssssssssss` sss` .sso\n" + " ossssssssss `yyo sys\n" + "`sssssssssss` `yys `yys\n" + "`sssssssssss: +yy/ +yy:\n" + " oyyyyyyyyyys. `oyy/` `+yy+\n" + " :yyyyyyyyyyyo. `+yhs:. `./shy/\n" + " oyyyyyyyyyyys:` .oyhys+:----/+syhy+. `\n" + " `syyyyyyyyyyyyo-` .:osyhhhhhyys+:``.:`\n" + " `oyyyyyyyyyyyyys+-`` `.----.```./oo.\n" + " /yhhhhhhhhhhhhhhyso+//://+osyhy/`\n" + " `/yhhhhhhhhhhhhhhhhhhhhhhhhy/`\n" + " `:oyhhhhhhhhhhhhhhhhhhyo:`\n" + " .:+syhhhhhhhhys+:-`\n" + " ``....``" + + + + ) + FF_LOGO_COLORS( + "34" //blue + ) + FF_LOGO_COLOR_KEYS("34"); //blue + FF_LOGO_COLOR_TITLE("34"); //blue + FF_LOGO_RETURN +} + static const FFlogo* getLogoPop() { FF_LOGO_INIT @@ -1522,6 +1800,56 @@ static const FFlogo* getLogoPop() FF_LOGO_RETURN } +static const FFlogo* getLogoParabola() +{ + FF_LOGO_INIT + FF_LOGO_NAMES("parabola", "parabola-gnulinux") + FF_LOGO_LINES( + " `.-. `.\n" + " `.` `:++. `-+o+.\n" + " `` `:+/. `:+/. `-+oooo+\n" + " ``-::-.:+/. `:+/. `-+oooooo+\n" + " `.-:///- ..` .-. `-+oooooooo-\n" + " `..-..` `+ooooooooo:\n" + "`` :oooooooo/\n" + " `ooooooo:\n" + " `oooooo:\n" + " -oooo+.\n" + " +ooo/`\n" + " -ooo-\n" + " `+o/.\n" + " /+-\n" + " //`\n" + " -." + ) + FF_LOGO_COLORS( + "35" //magenta + ) + FF_LOGO_COLOR_KEYS("35"); //magenta + FF_LOGO_COLOR_TITLE("35"); //magenta + FF_LOGO_RETURN +} + +static const FFlogo* getLogoParabolaSmall() +{ + FF_LOGO_INIT + FF_LOGO_NAMES("parabola_small", "parabola-gnulinux_small") + FF_LOGO_LINES( + " __ __ __ _\n" + ".`_//_//_/ / `.\n" + " / .`\n" + " / .`\n" + " /.`\n" + " /`" + ) + FF_LOGO_COLORS( + "35" //magenta + ) + FF_LOGO_COLOR_KEYS("35"); //magenta + FF_LOGO_COLOR_TITLE("35"); //magenta + FF_LOGO_RETURN +} + static const FFlogo* getLogoPopSmall() { FF_LOGO_INIT @@ -1683,7 +2011,7 @@ static const FFlogo* getLogoRockyLinux() "jliililiiilililiiili@` ~ililiiiiiL\n" "iiiliiiiliiiiiiili>` ~liililii\n" "liliiiliiilililii` -9liiiil\n" - "iiiiiliiliiiiii~ ''4lili\n" + "iiiiiliiliiiiii~ \"4lili\n" "4ililiiiiilil~| -w, )4lf\n" "-liiiiililiF' _liig, )'\n" " )iiiliii@` _QIililig,\n" @@ -1706,7 +2034,7 @@ static const FFlogo* getLogoRosaLinux() FF_LOGO_INIT FF_LOGO_NAMES("rosa", "rosa-linux", "rosalinux") FF_LOGO_LINES( - " ROSAROSAROSAROSAR\n" + " ROSAROSAROSAROSAR\n" " ROSA AROS\n" " ROS SAROSAROSAROSAR AROS\n" " RO ROSAROSAROSAROSAROSAR RO\n" @@ -1792,34 +2120,67 @@ static const FFlogo* getLogoSlackwareSmall() FF_LOGO_RETURN } +static const FFlogo* getLogoSolus() +{ + FF_LOGO_INIT + FF_LOGO_NAMES("solus", "solus-linux") + FF_LOGO_LINES( +"$2 -```````````\n" +" `-+/------------.`\n" +" .---:mNo---------------.\n" +" .-----yMMMy:---------------.\n" +" `------oMMMMMm/----------------`\n" +" .------/MMMMMMMN+----------------.\n" +" .------/NMMMMMMMMm-+/--------------.\n" +"`------/NMMMMMMMMMN-:mh/-------------`\n" +".-----/NMMMMMMMMMMM:-+MMd//oso/:-----.\n" +"-----/NMMMMMMMMMMMM+--mMMMh::smMmyo:--\n" +"----+NMMMMMMMMMMMMMo--yMMMMNo-:yMMMMd/.\n" +".--oMMMMMMMMMMMMMMMy--yMMMMMMh:-yMMMy-`\n" +"`-sMMMMMMMMMMMMMMMMh--dMMMMMMMd:/Ny+y.\n" +"`-/+osyhhdmmNNMMMMMm-/MMMMMMMmh+/ohm+\n" +" .------------:://+-/++++++$1oshddys:\n" +" -hhhhyyyyyyyyyyyhhhhddddhysssso-\n" +" `:ossssssyysssssssssssssssso:`\n" +" `:+ssssssssssssssssssss+-\n" +" `-/+ssssssssssso+/-`\n" +" `.-----..`\n" + ) + FF_LOGO_COLORS( + "34", //blue + "37" //white + ) + FF_LOGO_COLOR_KEYS("34"); //blue + FF_LOGO_COLOR_TITLE("37"); //white + FF_LOGO_RETURN +} + static const FFlogo* getLogoUbuntu() { FF_LOGO_INIT FF_LOGO_NAMES("ubuntu", "ubuntu-linux") FF_LOGO_LINES( " ....\n" - " .',:clooo: .:looooo:.\n" - " .;looooooooc .oooooooooo'\n" - " .;looooool:,''. :ooooooooooc\n" - " ;looool;. 'oooooooooo,\n" - " ;clool' .cooooooc. ,,\n" - " ... ...... .:oo,\n" + " .',:clooo: .:looooo:.\n" + " .;looooooooc .oooooooooo'\n" + " .;looooool:,''. :ooooooooooc\n" + " ;looool;. 'oooooooooo,\n" + " ;clool' .cooooooc. ,,\n" + " ... ...... .:oo,\n" " .;clol:,. .loooo'\n" - ":ooooooooo, 'ooool\n" + " :ooooooooo, 'ooool\n" "'ooooooooooo. loooo.\n" "'ooooooooool coooo.\n" " ,loooooooc. .loooo.\n" " .,;;;'. ;ooooc\n" - " ... ,ooool.\n" - " .cooooc. ..',,'. .cooo.\n" - " ;ooooo:. ;oooooooc. :l.\n" - " .coooooc,.. coooooooooo.\n" - " .:ooooooolc:. .ooooooooooo'\n" - " .':loooooo; ,oooooooooc\n" - " ..';::c' .;loooo:'\n" - " ." - - + " ... ,ooool.\n" + " .cooooc. ..',,'. .cooo.\n" + " ;ooooo:. ;oooooooc. :l.\n" + " .coooooc,.. coooooooooo.\n" + " .:ooooooolc:. .ooooooooooo'\n" + " .':loooooo; ,oooooooooc\n" + " ..';::c' .;loooo:'\n" + " ." ) FF_LOGO_COLORS( "31", //red @@ -1885,6 +2246,39 @@ static const FFlogo* getLogoUbuntuSmall() FF_LOGO_RETURN } +static const FFlogo* getLogoVanilla() +{ + + FF_LOGO_INIT + FF_LOGO_NAMES("vanilla", "vanilla-os","vanilla-linux"); + FF_LOGO_LINES( +" .----: \n" +" .-------.\n" +" :---::----:\n" +" .----::-----.\n" +" ......... :----::-----: ..:::-::::..\n" +".-----------------::------------------:\n" +" ----::-----------::----------::::---:\n" +" -----:::--------::-------:::-------\n" +" :------::::--::...:::::---------:\n" +" .---------::.. ..:---------.\n" +" .::-----::.. .::----::.\n" +" .:------:.......:-------:\n" +" .--------::::::::-:::-------.\n" +" .-------::-----.:-----::------.\n" +" -----::------: :------::-----\n" +" :--::--------: .-------::---:\n" +" :----------:: .:----------\n" +" :--------: :--------:" + ) + FF_LOGO_COLORS( + "33" //yellow + ) + FF_LOGO_COLOR_KEYS("33"); // yellow + FF_LOGO_COLOR_TITLE("33"); // yellow + FF_LOGO_RETURN +} + static const FFlogo* getLogoVoid() { FF_LOGO_INIT @@ -1990,6 +2384,7 @@ GetLogoMethod* ffLogoBuiltinGetAll() getLogoCelOS, getLogoCentOS, getLogoCentOSSmall, + getLogoCRUX, getLogoCrystalLinux, getLogoDebian, getLogoDevuan, @@ -1997,11 +2392,13 @@ GetLogoMethod* ffLogoBuiltinGetAll() getLogoDebianSmall, getLogoDeepin, getLogoEndeavour, + getLogoEnso, getLogoFedora, getLogoFedoraSmall, getLogoFedoraOld, getLogoFreeBSD, getLogoGaruda, + getLogoGarudaSmall, getLogoGentoo, getLogoGentooSmall, getLogoKDENeon, @@ -2016,15 +2413,23 @@ GetLogoMethod* ffLogoBuiltinGetAll() getLogoMintSmall, getLogoMintOld, getLogoMsys2, + getLogoWindows11, + getLogoWindows11Small, + getLogoWindows8, + getLogoWindows, getLogoNixOS, getLogoNixOsOld, getLogoNixOsSmall, + getLogoNobara, getLogoOpenSuse, getLogoOpenSuseSmall, getLogoOpenSuseLeap, getLogoOpenSuseTumbleweed, + getLogoOpenMandriva, getLogoPop, getLogoPopSmall, + getLogoParabola, + getLogoParabolaSmall, getLogoReborn, getLogoRebornSmall, getLogoRedHatEnterpriseLinux, @@ -2033,9 +2438,11 @@ GetLogoMethod* ffLogoBuiltinGetAll() getLogoRosaLinux, getLogoSlackware, getLogoSlackwareSmall, + getLogoSolus, getLogoUbuntu, getLogoUbuntuOld, getLogoUbuntuSmall, + getLogoVanilla, getLogoVoid, getLogoVoidSmall, getLogoZorin, diff --git a/src/logo/image/im6.c b/src/logo/image/im6.c index f6cbf2de0..a235bb867 100644 --- a/src/logo/image/im6.c +++ b/src/logo/image/im6.c @@ -14,7 +14,7 @@ static void* logoResize(const void* image, size_t width, size_t height, void* ex FFLogoImageResult ffLogoPrintImageIM6(FFinstance* instance, FFLogoRequestData* requestData) { - FF_LIBRARY_LOAD(imageMagick, &instance->config.libImageMagick, FF_LOGO_IMAGE_RESULT_INIT_ERROR, "libMagickCore-6.Q16HDRI.so", 8, "libMagickCore-6.Q16.so", 8) + FF_LIBRARY_LOAD(imageMagick, &instance->config.libImageMagick, FF_LOGO_IMAGE_RESULT_INIT_ERROR, "libMagickCore-6.Q16HDRI" FF_LIBRARY_EXTENSION, 8, "libMagickCore-6.Q16" FF_LIBRARY_EXTENSION, 8) FF_LIBRARY_LOAD_SYMBOL_ADDRESS(imageMagick, ffResizeImage, ResizeImage, FF_LOGO_IMAGE_RESULT_INIT_ERROR); FFIMData imData; diff --git a/src/logo/image/im7.c b/src/logo/image/im7.c index db4043465..d7cf4b6d0 100644 --- a/src/logo/image/im7.c +++ b/src/logo/image/im7.c @@ -14,7 +14,11 @@ static void* logoResize(const void* image, size_t width, size_t height, void* ex FFLogoImageResult ffLogoPrintImageIM7(FFinstance* instance, FFLogoRequestData* requestData) { - FF_LIBRARY_LOAD(imageMagick, &instance->config.libImageMagick, FF_LOGO_IMAGE_RESULT_INIT_ERROR, "libMagickCore-7.Q16HDRI.so", 11, "libMagickCore-7.Q16.so", 11) + FF_LIBRARY_LOAD(imageMagick, &instance->config.libImageMagick, FF_LOGO_IMAGE_RESULT_INIT_ERROR, + "libMagickCore-7.Q16HDRI" FF_LIBRARY_EXTENSION, 11, + "libMagickCore-7.Q16" FF_LIBRARY_EXTENSION, 11, + "libMagickCore-7.Q16HDRI-10" FF_LIBRARY_EXTENSION, -1 // Required for Windows + ) FF_LIBRARY_LOAD_SYMBOL_ADDRESS(imageMagick, ffResizeImage, ResizeImage, FF_LOGO_IMAGE_RESULT_INIT_ERROR); FFIMData imData; diff --git a/src/logo/image/image.c b/src/logo/image/image.c index d927735ad..aed67ca6a 100644 --- a/src/logo/image/image.c +++ b/src/logo/image/image.c @@ -2,6 +2,77 @@ #include "common/io.h" #include "common/printing.h" +static FFstrbuf base64Encode(FFstrbuf* in) +{ + const char* base64Chars = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/"; + + FFstrbuf out; + ffStrbufInitA(&out, 8 * (1 + in->length / 6)); + + unsigned val = 0; + int valb = -6; + for (uint32_t i = 0; i < in->length; ++i) + { + unsigned char c = (unsigned char) in->chars[i]; + val = (val << 8) + c; + valb += 8; + while (valb >= 0) + { + ffStrbufAppendC(&out, base64Chars[(val>>valb)&0x3F]); + valb -= 6; + } + } + if (valb > -6) ffStrbufAppendC(&out, base64Chars[((val<<8)>>(valb+8))&0x3F]); + while (out.length % 4) ffStrbufAppendC(&out, '='); + return out; +} + +static bool printImageIterm(FFinstance* instance) +{ + if(instance->config.logo.width == 0 || instance->config.logo.height == 0) + return false; + + FFstrbuf buf; + ffStrbufInit(&buf); + if(!ffAppendFileBuffer(instance->config.logo.source.chars, &buf)) + return false; + + ffPrintCharTimes(' ', instance->config.logo.paddingLeft); + FFstrbuf base64 = base64Encode(&buf); + printf("\033]1337;File=inline=1;width=%u;height=%u;preserveAspectRatio=%u:%s\a\033[9999999D\n\033[%uA", + (unsigned) instance->config.logo.width, + (unsigned) instance->config.logo.height, + (unsigned) instance->config.logo.preserveAspectRadio, + base64.chars, + (unsigned) instance->config.logo.height + ); + instance->state.logoWidth = instance->config.logo.width + instance->config.logo.paddingLeft + instance->config.logo.paddingRight; + instance->state.logoHeight = instance->config.logo.height; + + ffStrbufDestroy(&buf); + ffStrbufDestroy(&base64); + + return true; +} + +static bool printImageKittyDirect(FFinstance* instance) +{ + ffPrintCharTimes(' ', instance->config.logo.paddingLeft); + FFstrbuf base64 = base64Encode(&instance->config.logo.source); + printf("\033_Ga=T,f=100,t=f,c=%u,r=%u,C=1;%s\033\\\033[9999999D", + (unsigned) instance->config.logo.width, + (unsigned) instance->config.logo.height, + base64.chars + ); + + instance->state.logoWidth = instance->config.logo.width + instance->config.logo.paddingLeft + instance->config.logo.paddingRight; + instance->state.logoHeight = instance->config.logo.height; + + ffStrbufDestroy(&base64); + + return true; +} + #if defined(FF_HAVE_IMAGEMAGICK7) || defined(FF_HAVE_IMAGEMAGICK6) #define FF_KITTY_MAX_CHUNK_SIZE 4096 @@ -16,7 +87,22 @@ #include #include #include + +#ifndef _WIN32 #include +#else +#include + +static inline char* realpath(const char* restrict file_name, char* restrict resolved_name) +{ + char* result = _fullpath(resolved_name, file_name, _MAX_PATH); + if(result) + { + resolved_name[1] = resolved_name[0]; // Drive Name + resolved_name[0] = '/'; + } +} +#endif #ifdef FF_HAVE_ZLIB #include "common/library.h" @@ -25,7 +111,7 @@ static bool compressBlob(const FFinstance* instance, void** blob, size_t* length) { - FF_LIBRARY_LOAD(zlib, &instance->config.libZ, false, "libz.so", 2) + FF_LIBRARY_LOAD(zlib, &instance->config.libZ, false, "libz" FF_LIBRARY_EXTENSION, 2) FF_LIBRARY_LOAD_SYMBOL(zlib, compressBound, false) FF_LIBRARY_LOAD_SYMBOL(zlib, compress2, false) @@ -114,7 +200,7 @@ static void printImagePixels(FFinstance* instance, FFLogoRequestData* requestDat instance->state.logoWidth = requestData->logoCharacterWidth + instance->config.logo.paddingLeft + instance->config.logo.paddingRight; instance->state.logoHeight = requestData->logoCharacterHeight; - if(requestData->type == FF_LOGO_TYPE_KITTY) + if(requestData->type == FF_LOGO_TYPE_IMAGE_KITTY) instance->state.logoHeight -= 1; //Write cache files @@ -213,12 +299,15 @@ static bool printImageKitty(FFinstance* instance, FFLogoRequestData* requestData } #ifdef FF_HAVE_CHAFA -#include +#include static bool printImageChafa(FFinstance* instance, FFLogoRequestData* requestData, const ImageData* imageData) { - FF_LIBRARY_LOAD(chafa, &instance->config.libChafa, false, "libchafa.so", 1) + FF_LIBRARY_LOAD(chafa, &instance->config.libChafa, false, + "libchafa" FF_LIBRARY_EXTENSION, 1, + "libchafa-0" FF_LIBRARY_EXTENSION, -1 // Required for Windows + ) FF_LIBRARY_LOAD_SYMBOL(chafa, chafa_symbol_map_new, false) - FF_LIBRARY_LOAD_SYMBOL(chafa, chafa_symbol_map_add_by_tags, false) + FF_LIBRARY_LOAD_SYMBOL(chafa, chafa_symbol_map_apply_selectors, false) FF_LIBRARY_LOAD_SYMBOL(chafa, chafa_canvas_config_new, false) FF_LIBRARY_LOAD_SYMBOL(chafa, chafa_canvas_config_set_geometry, false) FF_LIBRARY_LOAD_SYMBOL(chafa, chafa_canvas_config_set_symbol_map, false) @@ -228,21 +317,50 @@ static bool printImageChafa(FFinstance* instance, FFLogoRequestData* requestData FF_LIBRARY_LOAD_SYMBOL(chafa, chafa_canvas_unref, false) FF_LIBRARY_LOAD_SYMBOL(chafa, chafa_canvas_config_unref, false) FF_LIBRARY_LOAD_SYMBOL(chafa, chafa_symbol_map_unref, false) - FF_LIBRARY_LOAD_SYMBOL(chafa, g_string_free, false) imageData->ffCopyMagickString(imageData->imageInfo->magick, "RGBA", 5); size_t length; void* blob = imageData->ffImageToBlob(imageData->imageInfo, imageData->image, &length, imageData->exceptionInfo); if(!checkAllocationResult(blob, length)) + { + dlclose(chafa); return false; + } ChafaSymbolMap* symbolMap = ffchafa_symbol_map_new(); - ffchafa_symbol_map_add_by_tags(symbolMap, CHAFA_SYMBOL_TAG_ALL); + GError* error = NULL; + if(!ffchafa_symbol_map_apply_selectors(symbolMap, instance->config.logo.chafaSymbols.chars, &error)) + fputs(error->message, stderr); ChafaCanvasConfig* canvasConfig = ffchafa_canvas_config_new(); ffchafa_canvas_config_set_geometry(canvasConfig, (gint) requestData->logoCharacterWidth, (gint) requestData->logoCharacterHeight); ffchafa_canvas_config_set_symbol_map(canvasConfig, symbolMap); + if(instance->config.logo.chafaFgOnly) + { + FF_LIBRARY_LOAD_SYMBOL_LAZY(chafa, chafa_canvas_config_set_fg_only_enabled); + if(ffchafa_canvas_config_set_fg_only_enabled) + ffchafa_canvas_config_set_fg_only_enabled(canvasConfig, true); + } + if(instance->config.logo.chafaCanvasMode < CHAFA_CANVAS_MODE_MAX) + { + FF_LIBRARY_LOAD_SYMBOL_LAZY(chafa, chafa_canvas_config_set_canvas_mode); + if(ffchafa_canvas_config_set_canvas_mode) + ffchafa_canvas_config_set_canvas_mode(canvasConfig, (ChafaCanvasMode) instance->config.logo.chafaCanvasMode); + } + if(instance->config.logo.chafaColorSpace < CHAFA_COLOR_SPACE_MAX) + { + FF_LIBRARY_LOAD_SYMBOL_LAZY(chafa, chafa_canvas_config_set_color_space) + if(ffchafa_canvas_config_set_color_space) + ffchafa_canvas_config_set_color_space(canvasConfig, (ChafaColorSpace) instance->config.logo.chafaColorSpace); + } + if(instance->config.logo.chafaDitherMode < CHAFA_DITHER_MODE_MAX) + { + FF_LIBRARY_LOAD_SYMBOL_LAZY(chafa, chafa_canvas_config_set_dither_mode) + if(ffchafa_canvas_config_set_dither_mode) + ffchafa_canvas_config_set_dither_mode(canvasConfig, (ChafaDitherMode) instance->config.logo.chafaDitherMode); + } + ChafaCanvas* canvas = ffchafa_canvas_new(canvasConfig); ffchafa_canvas_draw_all_pixels( canvas, @@ -262,10 +380,21 @@ static bool printImageChafa(FFinstance* instance, FFLogoRequestData* requestData ffLogoPrintChars(instance, result.chars, false); writeCacheStrbuf(requestData, &result, FF_CACHE_FILE_CHAFA); - ffg_string_free(str, TRUE); + // FIXME: These functions must be imported from `libglib` dlls on Windows + FF_LIBRARY_LOAD_SYMBOL_LAZY(chafa, g_string_free); + if(ffg_string_free) + ffg_string_free(str, TRUE); + if(error) + { + FF_LIBRARY_LOAD_SYMBOL_LAZY(chafa, g_error_free) + if(ffg_error_free) + ffg_error_free(error); + } + ffchafa_canvas_unref(canvas); ffchafa_canvas_config_unref(canvasConfig); ffchafa_symbol_map_unref(symbolMap); + dlclose(chafa); return true; } @@ -273,6 +402,8 @@ static bool printImageChafa(FFinstance* instance, FFLogoRequestData* requestData FFLogoImageResult ffLogoPrintImageImpl(FFinstance* instance, FFLogoRequestData* requestData, const FFIMData* imData) { + FF_LIBRARY_LOAD_SYMBOL(imData->library, MagickCoreGenesis, FF_LOGO_IMAGE_RESULT_INIT_ERROR); + FF_LIBRARY_LOAD_SYMBOL(imData->library, MagickCoreTerminus, FF_LOGO_IMAGE_RESULT_INIT_ERROR); FF_LIBRARY_LOAD_SYMBOL(imData->library, AcquireExceptionInfo, FF_LOGO_IMAGE_RESULT_INIT_ERROR) FF_LIBRARY_LOAD_SYMBOL(imData->library, DestroyExceptionInfo, FF_LOGO_IMAGE_RESULT_INIT_ERROR) FF_LIBRARY_LOAD_SYMBOL(imData->library, AcquireImageInfo, FF_LOGO_IMAGE_RESULT_INIT_ERROR) @@ -286,14 +417,20 @@ FFLogoImageResult ffLogoPrintImageImpl(FFinstance* instance, FFLogoRequestData* FF_LIBRARY_LOAD_SYMBOL_VAR(imData->library, imageData, ImageToBlob, FF_LOGO_IMAGE_RESULT_INIT_ERROR) FF_LIBRARY_LOAD_SYMBOL_VAR(imData->library, imageData, Base64Encode, FF_LOGO_IMAGE_RESULT_INIT_ERROR) + ffMagickCoreGenesis(NULL, MagickFalse); + imageData.exceptionInfo = ffAcquireExceptionInfo(); if(imageData.exceptionInfo == NULL) + { + ffMagickCoreTerminus(); return FF_LOGO_IMAGE_RESULT_RUN_ERROR; + } ImageInfo* imageInfoIn = ffAcquireImageInfo(); if(imageInfoIn == NULL) { ffDestroyExceptionInfo(imageData.exceptionInfo); + ffMagickCoreTerminus(); return FF_LOGO_IMAGE_RESULT_RUN_ERROR; } @@ -305,6 +442,7 @@ FFLogoImageResult ffLogoPrintImageImpl(FFinstance* instance, FFLogoRequestData* if(imageData.image == NULL) { ffDestroyExceptionInfo(imageData.exceptionInfo); + ffMagickCoreTerminus(); return FF_LOGO_IMAGE_RESULT_RUN_ERROR; } @@ -325,6 +463,7 @@ FFLogoImageResult ffLogoPrintImageImpl(FFinstance* instance, FFLogoRequestData* { ffDestroyImage(imageData.image); ffDestroyExceptionInfo(imageData.exceptionInfo); + ffMagickCoreTerminus(); return FF_LOGO_IMAGE_RESULT_RUN_ERROR; } @@ -333,6 +472,7 @@ FFLogoImageResult ffLogoPrintImageImpl(FFinstance* instance, FFLogoRequestData* if(resized == NULL) { ffDestroyExceptionInfo(imageData.exceptionInfo); + ffMagickCoreTerminus(); return FF_LOGO_IMAGE_RESULT_RUN_ERROR; } imageData.image = resized; @@ -342,24 +482,26 @@ FFLogoImageResult ffLogoPrintImageImpl(FFinstance* instance, FFLogoRequestData* { ffDestroyImage(imageData.image); ffDestroyExceptionInfo(imageData.exceptionInfo); + ffMagickCoreTerminus(); return FF_LOGO_IMAGE_RESULT_RUN_ERROR; } bool printSuccessful = false; - if(requestData->type == FF_LOGO_TYPE_CHAFA) + if(requestData->type == FF_LOGO_TYPE_IMAGE_CHAFA) { #ifdef FF_HAVE_CHAFA printSuccessful = printImageChafa(instance, requestData, &imageData); #endif } - else if(requestData->type == FF_LOGO_TYPE_KITTY) + else if(requestData->type == FF_LOGO_TYPE_IMAGE_KITTY) printSuccessful = printImageKitty(instance, requestData, &imageData); - else if(requestData->type == FF_LOGO_TYPE_SIXEL) + else if(requestData->type == FF_LOGO_TYPE_IMAGE_SIXEL) printSuccessful = printImageSixel(instance, requestData, &imageData); ffDestroyImageInfo(imageData.imageInfo); ffDestroyImage(imageData.image); ffDestroyExceptionInfo(imageData.exceptionInfo); + ffMagickCoreTerminus(); return printSuccessful ? FF_LOGO_IMAGE_RESULT_SUCCESS : FF_LOGO_IMAGE_RESULT_RUN_ERROR; } @@ -407,7 +549,7 @@ static bool printCachedChars(FFinstance* instance, FFLogoRequestData* requestDat FFstrbuf content; ffStrbufInitA(&content, 32768); - if(requestData->type == FF_LOGO_TYPE_CHAFA) + if(requestData->type == FF_LOGO_TYPE_IMAGE_CHAFA) readCachedStrbuf(requestData, &content, FF_CACHE_FILE_CHAFA); if(content.length == 0) @@ -440,13 +582,13 @@ static bool printCachedPixel(FFinstance* instance, FFLogoRequestData* requestDat } int fd = -1; - if(requestData->type == FF_LOGO_TYPE_KITTY) + if(requestData->type == FF_LOGO_TYPE_IMAGE_KITTY) { fd = getCacheFD(requestData, FF_CACHE_FILE_KITTY_COMPRESSED); if(fd == -1) fd = getCacheFD(requestData, FF_CACHE_FILE_KITTY_UNCOMPRESSED); } - else if(requestData->type == FF_LOGO_TYPE_SIXEL) + else if(requestData->type == FF_LOGO_TYPE_IMAGE_SIXEL) fd = getCacheFD(requestData, FF_CACHE_FILE_SIXEL); if(fd == -1) @@ -473,7 +615,7 @@ static bool printCachedPixel(FFinstance* instance, FFLogoRequestData* requestDat static bool printCached(FFinstance* instance, FFLogoRequestData* requestData) { - if(requestData->type == FF_LOGO_TYPE_CHAFA) + if(requestData->type == FF_LOGO_TYPE_IMAGE_CHAFA) return printCachedChars(instance, requestData); else return printCachedPixel(instance, requestData); @@ -481,6 +623,8 @@ static bool printCached(FFinstance* instance, FFLogoRequestData* requestData) static bool getCharacterPixelDimensions(FFLogoRequestData* requestData) { + #ifndef _WIN32 + struct winsize winsize; //Initialize every member to 0, because it isn't guaranteed that every terminal sets them all @@ -500,40 +644,60 @@ static bool getCharacterPixelDimensions(FFLogoRequestData* requestData) requestData->characterPixelWidth = winsize.ws_xpixel / (double) winsize.ws_col; requestData->characterPixelHeight = winsize.ws_ypixel / (double) winsize.ws_row; + #else + + CONSOLE_FONT_INFO cfi; + if(GetCurrentConsoleFont(GetStdHandle(STD_OUTPUT_HANDLE), FALSE, &cfi) == FALSE) // Only works for ConHost + return false; + + requestData->characterPixelWidth = cfi.dwFontSize.X; + requestData->characterPixelHeight = cfi.dwFontSize.Y; + + #endif + return requestData->characterPixelWidth > 1.0 && requestData->characterPixelHeight > 1.0; } -bool ffLogoPrintImageIfExists(FFinstance* instance, FFLogoType type) +static bool printImageIfExistsSlowPath(FFinstance* instance, FFLogoType type) { - //Performance optimisation - #ifndef FF_HAVE_CHAFA - if(type == FF_LOGO_TYPE_CHAFA) - return false; - #endif - FFLogoRequestData requestData; requestData.type = type; requestData.characterPixelWidth = 1; requestData.characterPixelHeight = 1; if( - (type != FF_LOGO_TYPE_CHAFA || instance->config.logo.width == 0 || instance->config.logo.height == 0) && + (type != FF_LOGO_TYPE_IMAGE_CHAFA || instance->config.logo.width == 0 || instance->config.logo.height == 0) && !getCharacterPixelDimensions(&requestData) - ) + ) { + fputs("Logo: getCharacterPixelDimensions() failed", stderr); return false; + } requestData.logoPixelWidth = simpleCeil((double) instance->config.logo.width * requestData.characterPixelWidth); requestData.logoPixelHeight = simpleCeil((double) instance->config.logo.height * requestData.characterPixelHeight); ffStrbufInitA(&requestData.cacheDir, PATH_MAX * 2); - ffStrbufAppend(&requestData.cacheDir, &instance->state.cacheDir); - ffStrbufAppendS(&requestData.cacheDir, "images"); + + #if !(defined(_WIN32) || defined(__APPLE__) || defined(__ANDROID__)) + ffStrbufAppendS(&requestData.cacheDir, getenv("XDG_CACHE_HOME")); + #endif + + if(requestData.cacheDir.length == 0) + { + ffStrbufAppendS(&requestData.cacheDir, instance->state.passwd->pw_dir); + ffStrbufAppendS(&requestData.cacheDir, "/.cache/"); + } + else + ffStrbufEnsureEndsWithC(&requestData.cacheDir, '/'); + + ffStrbufAppendS(&requestData.cacheDir, "fastfetch"); ffStrbufEnsureFree(&requestData.cacheDir, PATH_MAX); if(realpath(instance->config.logo.source.chars, requestData.cacheDir.chars + requestData.cacheDir.length) == NULL) { //We can safely return here, because if realpath failed, we surely won't be able to read the file ffStrbufDestroy(&requestData.cacheDir); + fputs("Logo: Querying realpath of the image source failed", stderr); return false; } ffStrbufRecalculateLength(&requestData.cacheDir); @@ -562,13 +726,53 @@ bool ffLogoPrintImageIfExists(FFinstance* instance, FFLogoType type) #endif ffStrbufDestroy(&requestData.cacheDir); - return result == FF_LOGO_IMAGE_RESULT_SUCCESS; + + switch(result) + { + case FF_LOGO_IMAGE_RESULT_INIT_ERROR: + fputs("Logo: Init Image Magick library failed\n", stderr); + return false; + case FF_LOGO_IMAGE_RESULT_RUN_ERROR: + fputs("Logo: Failed to load / convert the image source\n", stderr); + return false; + default: + return true; + } } -#else //FF_HAVE_IMAGEMAGICK{6, 7} +#endif //FF_HAVE_IMAGEMAGICK{6, 7} + bool ffLogoPrintImageIfExists(FFinstance* instance, FFLogoType type) { - FF_UNUSED(instance, type); - return false; + if(!ffFileExists(instance->config.logo.source.chars, S_IFREG)) + { + fputs("Logo: Image file not found\n", stderr); + return false; + } + + if(type == FF_LOGO_TYPE_IMAGE_ITERM) + return printImageIterm(instance); + + if( + type == FF_LOGO_TYPE_IMAGE_KITTY && + ffStrbufEndsWithIgnCaseS(&instance->config.logo.source, ".png") && + instance->config.logo.width && + instance->config.logo.height + ) + return printImageKittyDirect(instance); + + #ifndef FF_HAVE_CHAFA + if(type == FF_LOGO_TYPE_IMAGE_CHAFA) + { + fputs("Logo: Fastfetch was built without Chafa support\n", stderr); + return false; + } + #endif + + #if defined(FF_HAVE_IMAGEMAGICK7) || defined(FF_HAVE_IMAGEMAGICK6) + return printImageIfExistsSlowPath(instance, type); + #else + fputs("Logo: Fastfetch was built without ImageMagick support\n", stderr); + return false; + #endif } -#endif //FF_HAVE_IMAGEMAGICK{6, 7} diff --git a/src/logo/image/image.h b/src/logo/image/image.h index 67fbbcdc1..83d77131a 100644 --- a/src/logo/image/image.h +++ b/src/logo/image/image.h @@ -11,7 +11,7 @@ typedef enum FFLogoImageResult { FF_LOGO_IMAGE_RESULT_SUCCESS, //Logo printed FF_LOGO_IMAGE_RESULT_INIT_ERROR, //Failed to load library, try again with next IM version - FF_LOGO_IMAGE_RESULT_RUN_ERROR //Failed to load / convert image, cancle whole sixel code + FF_LOGO_IMAGE_RESULT_RUN_ERROR //Failed to load / convert image, cancel whole sixel code } FFLogoImageResult; typedef struct FFLogoRequestData diff --git a/src/logo/logo.c b/src/logo/logo.c index e0340d571..3c48e0854 100644 --- a/src/logo/logo.c +++ b/src/logo/logo.c @@ -2,7 +2,8 @@ #include "common/io.h" #include "common/printing.h" #include "detection/os/os.h" -#include "detection/terminalshell.h" +#include "detection/terminalshell/terminalshell.h" +#include "util/textModifier.h" #include #include @@ -265,6 +266,11 @@ static inline void logoPrintDetected(FFinstance* instance) logoPrintStruct(instance, logoGetBuiltinDetected(instance)); } +static void logoPrintData(FFinstance* instance, bool doColorReplacement) { + ffLogoPrintChars(instance, instance->config.logo.source.chars, doColorReplacement); + logoApplyColorsDetected(instance); +} + static bool logoPrintFileIfExists(FFinstance* instance, bool doColorReplacement) { FFstrbuf content; @@ -293,14 +299,18 @@ static bool logoPrintImageIfExists(FFinstance* instance, FFLogoType logo) static void logoPrintKnownType(FFinstance* instance) { - bool successfull; + bool successfull = true; if(instance->config.logo.type == FF_LOGO_TYPE_BUILTIN) successfull = logoPrintBuiltinIfExists(instance, instance->config.logo.source.chars); else if(instance->config.logo.type == FF_LOGO_TYPE_FILE) successfull = logoPrintFileIfExists(instance, true); - else if(instance->config.logo.type == FF_LOGO_TYPE_RAW) + else if(instance->config.logo.type == FF_LOGO_TYPE_FILE_RAW) successfull = logoPrintFileIfExists(instance, false); + else if(instance->config.logo.type == FF_LOGO_TYPE_DATA) + logoPrintData(instance, true); + else if(instance->config.logo.type == FF_LOGO_TYPE_DATA_RAW) + logoPrintData(instance, false); else //image successfull = logoPrintImageIfExists(instance, instance->config.logo.type); @@ -348,7 +358,7 @@ void ffLogoPrint(FFinstance* instance) ffStrbufIgnCaseCompS(&terminalShell->terminalProcessName, "wayst") == 0; //Try to load the logo as an image. If it succeeds, print it and return. - if(logoPrintImageIfExists(instance, supportsKitty ? FF_LOGO_TYPE_KITTY : FF_LOGO_TYPE_CHAFA)) + if(logoPrintImageIfExists(instance, supportsKitty ? FF_LOGO_TYPE_IMAGE_KITTY : FF_LOGO_TYPE_IMAGE_CHAFA)) return; //Try to load the logo as a file. If it succeeds, print it and return. diff --git a/src/modules/battery.c b/src/modules/battery.c index 58773bf0a..97c603d9a 100644 --- a/src/modules/battery.c +++ b/src/modules/battery.c @@ -1,11 +1,12 @@ #include "fastfetch.h" #include "common/printing.h" +#include "common/bar.h" #include "detection/battery/battery.h" #define FF_BATTERY_MODULE_NAME "Battery" #define FF_BATTERY_NUM_FORMAT_ARGS 5 -static void printBattery(FFinstance* instance, BatteryResult* result, uint8_t index) +static void printBattery(FFinstance* instance, BatteryResult* result, uint8_t index) { if(instance->config.battery.outputFormat.length == 0) { @@ -15,27 +16,47 @@ static void printBattery(FFinstance* instance, BatteryResult* result, uint8_t i result->status.length > 0 && ffStrbufIgnCaseCompS(&result->status, "Unknown") != 0; - if(result->capacity.length > 0) - { - ffStrbufWriteTo(&result->capacity, stdout); - putchar('%'); + FFstrbuf str; + ffStrbufInit(&str); - if(showStatus) - fputs(" [", stdout); + if(result->capacity >= 0) + { + if(instance->config.percentType & FF_PERCENTAGE_TYPE_BAR_BIT) + { + if(result->capacity <= 20) + ffAppendPercentBar(instance, &str, (uint8_t)result->capacity, 10, 10, 0); + else if(result->capacity <= 50) + ffAppendPercentBar(instance, &str, (uint8_t)result->capacity, 10, 0, 10); + else + ffAppendPercentBar(instance, &str, (uint8_t)result->capacity, 0, 10, 10); + } + + if(instance->config.percentType & FF_PERCENTAGE_TYPE_NUM_BIT) + { + if(str.length > 0) + ffStrbufAppendC(&str, ' '); + ffStrbufAppendF(&str, "%.0f%%", result->capacity); + } } if(showStatus) { - ffStrbufWriteTo(&result->status, stdout); - - if(result->capacity.length > 0) - putchar(']'); + if(str.length > 0) + ffStrbufAppendF(&str, " [%s]", result->status.chars); + else + ffStrbufAppend(&str, &result->status); } if(result->temperature == result->temperature) //FF_BATTERY_TEMP_UNSET - printf(" - %.1f°C", result->temperature); + { + if(str.length > 0) + ffStrbufAppendS(&str, " - "); - putchar('\n'); + ffStrbufAppendF(&str, "%.1f°C", result->temperature); + } + + ffStrbufPutTo(&str, stdout); + ffStrbufDestroy(&str); } else { @@ -43,7 +64,7 @@ static void printBattery(FFinstance* instance, BatteryResult* result, uint8_t i {FF_FORMAT_ARG_TYPE_STRBUF, &result->manufacturer}, {FF_FORMAT_ARG_TYPE_STRBUF, &result->modelName}, {FF_FORMAT_ARG_TYPE_STRBUF, &result->technology}, - {FF_FORMAT_ARG_TYPE_STRBUF, &result->capacity}, + {FF_FORMAT_ARG_TYPE_DOUBLE, &result->capacity}, {FF_FORMAT_ARG_TYPE_STRBUF, &result->status}, {FF_FORMAT_ARG_TYPE_DOUBLE, &result->temperature}, }); @@ -71,7 +92,6 @@ void ffPrintBattery(FFinstance* instance) ffStrbufDestroy(&result->manufacturer); ffStrbufDestroy(&result->modelName); ffStrbufDestroy(&result->technology); - ffStrbufDestroy(&result->capacity); ffStrbufDestroy(&result->status); } if(results.length == 0) diff --git a/src/modules/bios.c b/src/modules/bios.c new file mode 100644 index 000000000..7b3853355 --- /dev/null +++ b/src/modules/bios.c @@ -0,0 +1,46 @@ +#include "fastfetch.h" +#include "common/printing.h" +#include "detection/bios/bios.h" + +#define FF_BIOS_MODULE_NAME "Bios" +#define FF_BIOS_NUM_FORMAT_ARGS 4 + +void ffPrintBios(FFinstance* instance) +{ + FFBiosResult result; + ffDetectBios(&result); + + if(result.error.length > 0) + { + ffPrintError(instance, FF_BIOS_MODULE_NAME, 0, &instance->config.bios, "%*s", result.error.length, result.error.chars); + goto exit; + } + + if(result.biosRelease.length == 0) + { + ffPrintError(instance, FF_BIOS_MODULE_NAME, 0, &instance->config.bios, "bios_release is not set."); + goto exit; + } + + if(instance->config.bios.outputFormat.length == 0) + { + ffPrintLogoAndKey(instance, FF_BIOS_MODULE_NAME, 0, &instance->config.bios.key); + puts(result.biosRelease.chars); + } + else + { + ffPrintFormat(instance, FF_BIOS_MODULE_NAME, 0, &instance->config.bios, FF_BIOS_NUM_FORMAT_ARGS, (FFformatarg[]) { + {FF_FORMAT_ARG_TYPE_STRBUF, &result.biosDate}, + {FF_FORMAT_ARG_TYPE_STRBUF, &result.biosRelease}, + {FF_FORMAT_ARG_TYPE_STRBUF, &result.biosVendor}, + {FF_FORMAT_ARG_TYPE_STRBUF, &result.biosVersion}, + }); + } + +exit: + ffStrbufDestroy(&result.biosDate); + ffStrbufDestroy(&result.biosRelease); + ffStrbufDestroy(&result.biosVendor); + ffStrbufDestroy(&result.biosVersion); + ffStrbufDestroy(&result.error); +} diff --git a/src/modules/board.c b/src/modules/board.c new file mode 100644 index 000000000..558653892 --- /dev/null +++ b/src/modules/board.c @@ -0,0 +1,44 @@ +#include "fastfetch.h" +#include "common/printing.h" +#include "detection/board/board.h" + +#define FF_BOARD_MODULE_NAME "Board" +#define FF_BOARD_NUM_FORMAT_ARGS 3 + +void ffPrintBoard(FFinstance* instance) +{ + FFBoardResult result; + ffDetectBoard(&result); + + if(result.error.length > 0) + { + ffPrintError(instance, FF_BOARD_MODULE_NAME, 0, &instance->config.board, "%*s", result.error.length, result.error.chars); + goto exit; + } + + if(result.boardName.length == 0) + { + ffPrintError(instance, FF_BOARD_MODULE_NAME, 0, &instance->config.board, "board_name is not set."); + goto exit; + } + + if(instance->config.board.outputFormat.length == 0) + { + ffPrintLogoAndKey(instance, FF_BOARD_MODULE_NAME, 0, &instance->config.board.key); + puts(result.boardName.chars); + } + else + { + ffPrintFormat(instance, FF_BOARD_MODULE_NAME, 0, &instance->config.board, FF_BOARD_NUM_FORMAT_ARGS, (FFformatarg[]) { + {FF_FORMAT_ARG_TYPE_STRBUF, &result.boardName}, + {FF_FORMAT_ARG_TYPE_STRBUF, &result.boardVendor}, + {FF_FORMAT_ARG_TYPE_STRBUF, &result.boardVersion}, + }); + } + +exit: + ffStrbufDestroy(&result.boardName); + ffStrbufDestroy(&result.boardVendor); + ffStrbufDestroy(&result.boardVersion); + ffStrbufDestroy(&result.error); +} diff --git a/src/modules/chassis.c b/src/modules/chassis.c new file mode 100644 index 000000000..03a030c85 --- /dev/null +++ b/src/modules/chassis.c @@ -0,0 +1,53 @@ +#include "fastfetch.h" +#include "common/printing.h" +#include "detection/chassis/chassis.h" + +#define FF_CHASSIS_MODULE_NAME "Chassis" +#define FF_CHASSIS_NUM_FORMAT_ARGS 3 + +void ffPrintChassis(FFinstance* instance) +{ + FFChassisResult result; + ffDetectChassis(&result); + + if(result.error.length > 0) + { + ffPrintError(instance, FF_CHASSIS_MODULE_NAME, 0, &instance->config.chassis, "%*s", result.error.length, result.error.chars); + goto exit; + } + + if(result.chassisType.length == 0) + { + ffPrintError(instance, FF_CHASSIS_MODULE_NAME, 0, &instance->config.host, "chassis_type is not set by O.E.M."); + return; + } + + if(instance->config.chassis.outputFormat.length == 0) + { + ffPrintLogoAndKey(instance, FF_CHASSIS_MODULE_NAME, 0, &instance->config.host.key); + + FFstrbuf output; + ffStrbufInitCopy(&output, &result.chassisType); + + if(result.chassisVersion.length > 0) + ffStrbufAppendF(&output, " (%s)", result.chassisVersion.chars); + + ffStrbufPutTo(&output, stdout); + + ffStrbufDestroy(&output); + } + else + { + ffPrintFormat(instance, FF_CHASSIS_MODULE_NAME, 0, &instance->config.chassis, FF_CHASSIS_NUM_FORMAT_ARGS, (FFformatarg[]) { + {FF_FORMAT_ARG_TYPE_STRBUF, &result.chassisType}, + {FF_FORMAT_ARG_TYPE_STRBUF, &result.chassisVendor}, + {FF_FORMAT_ARG_TYPE_STRBUF, &result.chassisVersion}, + }); + } + +exit: + ffStrbufDestroy(&result.chassisType); + ffStrbufDestroy(&result.chassisVendor); + ffStrbufDestroy(&result.chassisVersion); + ffStrbufDestroy(&result.error); +} diff --git a/src/modules/colors.c b/src/modules/colors.c index 626ced7b0..40a936e00 100644 --- a/src/modules/colors.c +++ b/src/modules/colors.c @@ -1,5 +1,6 @@ #include "fastfetch.h" #include "common/printing.h" +#include "util/textModifier.h" void ffPrintColors(FFinstance* instance) { diff --git a/src/modules/cpu.c b/src/modules/cpu.c index 89c40fb99..eb38a9805 100644 --- a/src/modules/cpu.c +++ b/src/modules/cpu.c @@ -1,6 +1,5 @@ #include "fastfetch.h" #include "common/printing.h" -#include "common/caching.h" #include "detection/cpu/cpu.h" #define FF_CPU_MODULE_NAME "CPU" diff --git a/src/modules/cpuUsage.c b/src/modules/cpuUsage.c index a41735c82..3e4165b90 100644 --- a/src/modules/cpuUsage.c +++ b/src/modules/cpuUsage.c @@ -1,66 +1,43 @@ #include "fastfetch.h" #include "common/printing.h" +#include "common/bar.h" #include "detection/cpuUsage/cpuUsage.h" -#include -#include #define FF_CPU_USAGE_MODULE_NAME "CPU Usage" #define FF_CPU_USAGE_NUM_FORMAT_ARGS 1 -time_t getTimeInMs() -{ - struct timeval timeNow; - gettimeofday(&timeNow, NULL); - return (timeNow.tv_sec * 1000) + (timeNow.tv_usec / 1000); -} - -static long inUseAll1, totalAll1, startTime; - -void ffPrepareCPUUsage() -{ - startTime = getTimeInMs(); - ffGetCpuUsageInfo(&inUseAll1, &totalAll1); -} - void ffPrintCPUUsage(FFinstance* instance) { - const char* error = NULL; - if(startTime == 0) - { - error = ffGetCpuUsageInfo(&inUseAll1, &totalAll1); - if(error) - goto error; - nanosleep(&(struct timespec){ 1, 0 }, NULL); - } - else - { - time_t duration = getTimeInMs() - startTime; - if(duration < 1000) - nanosleep(&(struct timespec){ 0, (1000 - duration) * 1000000L }, NULL); - } - - long inUseAll2, totalAll2; - error = ffGetCpuUsageInfo(&inUseAll2, &totalAll2); + double percentage = 0.0/0.0; + const char* error = ffGetCpuUsageResult(&percentage); if(error) { - error: ffPrintError(instance, FF_CPU_USAGE_MODULE_NAME, 0, &instance->config.cpu, "%s", error); return; } - double cpuPercent = (double)(inUseAll2 - inUseAll1) / (double)(totalAll2 - totalAll1) * 100; - if(instance->config.cpuUsage.outputFormat.length == 0) { ffPrintLogoAndKey(instance, FF_CPU_USAGE_MODULE_NAME, 0, &instance->config.cpuUsage.key); - printf("%.2lf%%\n", cpuPercent); + FFstrbuf str; + ffStrbufInit(&str); + if(instance->config.percentType & FF_PERCENTAGE_TYPE_BAR_BIT) + ffAppendPercentBar(instance, &str, (uint8_t)percentage, 0, 5, 8); + if(instance->config.percentType & FF_PERCENTAGE_TYPE_NUM_BIT) + { + if(str.length > 0) + ffStrbufAppendC(&str, ' '); + ffStrbufAppendF(&str, "%.2lf%%", percentage); + } + ffStrbufPutTo(&str, stdout); + ffStrbufDestroy(&str); } else { ffPrintFormat(instance, FF_CPU_USAGE_MODULE_NAME, 0, &instance->config.cpuUsage, FF_CPU_USAGE_NUM_FORMAT_ARGS, (FFformatarg[]){ - {FF_FORMAT_ARG_TYPE_DOUBLE, &cpuPercent} + {FF_FORMAT_ARG_TYPE_DOUBLE, &percentage} }); } } diff --git a/src/modules/cursor.c b/src/modules/cursor.c index c0f572823..a5ddc90be 100644 --- a/src/modules/cursor.c +++ b/src/modules/cursor.c @@ -1,255 +1,55 @@ #include "fastfetch.h" -#include "common/properties.h" -#include "common/printing.h" -#include "common/parsing.h" -#include "common/settings.h" -#include "detection/gtk.h" -#include "detection/displayserver/displayserver.h" -#include +#include "common/printing.h" +#include "detection/cursor/cursor.h" #define FF_CURSOR_MODULE_NAME "Cursor" #define FF_CURSOR_NUM_FORMAT_ARGS 2 -#if !(defined(__ANDROID__) || defined(__APPLE__)) - -static void printCursor(FFinstance* instance, FFstrbuf* cursorTheme, const FFstrbuf* cursorSize) +static void printCursor(FFinstance* instance, FFCursorResult* cursor) { - ffStrbufRemoveIgnCaseEndS(cursorTheme, "cursors"); - ffStrbufRemoveIgnCaseEndS(cursorTheme, "cursor"); - ffStrbufTrimRight(cursorTheme, '_'); - ffStrbufTrimRight(cursorTheme, '-'); - if(cursorTheme->length == 0) - ffStrbufAppendS(cursorTheme, "default"); + ffStrbufRemoveIgnCaseEndS(&cursor->theme, "cursors"); + ffStrbufRemoveIgnCaseEndS(&cursor->theme, "cursor"); + ffStrbufTrimRight(&cursor->theme, '_'); + ffStrbufTrimRight(&cursor->theme, '-'); + if(cursor->theme.length == 0) + ffStrbufAppendS(&cursor->theme, "default"); if(instance->config.cursor.outputFormat.length == 0) { ffPrintLogoAndKey(instance, FF_CURSOR_MODULE_NAME, 0, &instance->config.cursor.key); - ffStrbufWriteTo(cursorTheme, stdout); + ffStrbufWriteTo(&cursor->theme, stdout); - if(cursorSize != NULL && cursorSize->length > 0) - { - fputs(" (", stdout); - ffStrbufWriteTo(cursorSize, stdout); - fputs("px)", stdout); - } + if(cursor->size.length > 0) + printf(" (%spx)", cursor->size.chars); putchar('\n'); } else { ffPrintFormat(instance, FF_CURSOR_MODULE_NAME, 0, &instance->config.cursor, FF_CURSOR_NUM_FORMAT_ARGS, (FFformatarg[]){ - {FF_FORMAT_ARG_TYPE_STRBUF, cursorTheme}, - {FF_FORMAT_ARG_TYPE_STRBUF, cursorSize} + {FF_FORMAT_ARG_TYPE_STRBUF, &cursor->theme}, + {FF_FORMAT_ARG_TYPE_STRBUF, &cursor->size} }); } } -static void printCursorGTK(FFinstance* instance) -{ - 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) - { - ffPrintError(instance, FF_CURSOR_MODULE_NAME, 0, &instance->config.cursor, "Couldn't detect GTK Cursor"); - return; - } - - //gtk->cursor is const, so we don't want to modify it - FFstrbuf theme; - ffStrbufInitCopy(&theme, >k->cursor); - - printCursor(instance, &theme, >k->cursorSize); - - ffStrbufDestroy(&theme); -} - -static void printCursorXFCE(FFinstance* instance) -{ - FFstrbuf cursorTheme; - ffStrbufInit(&cursorTheme); - - ffStrbufAppendS(&cursorTheme, ffSettingsGetXFConf(instance, "xsettings", "/Gtk/CursorThemeName", FF_VARIANT_TYPE_STRING).strValue); - - if(cursorTheme.length == 0) - { - ffPrintError(instance, FF_CURSOR_MODULE_NAME, 0, &instance->config.cursor, "Couldn't find xfce cursor in xfconf (xsettings::/Gtk/CursorThemeName)"); - return; - } - - FFstrbuf cursorSize; - ffStrbufInit(&cursorSize); - int cursorSizeVal = ffSettingsGetXFConf(instance, "xsettings", "/Gtk/CursorThemeSize", FF_VARIANT_TYPE_INT).intValue; - if(cursorSizeVal > 0) - ffStrbufAppendF(&cursorSize, "%i", cursorSizeVal); - - printCursor(instance, &cursorTheme, &cursorSize); - ffStrbufDestroy(&cursorTheme); - ffStrbufDestroy(&cursorSize); -} - -static void printCursorFromConfigFile(FFinstance* instance, const char* relativeFilePath, const char* themeStart, const char* themeDefault, const char* sizeStart, const char* sizeDefault) -{ - FFstrbuf cursorTheme; - ffStrbufInit(&cursorTheme); - - FFstrbuf cursorSize; - ffStrbufInit(&cursorSize); - - if(ffParsePropFileConfigValues(instance, relativeFilePath, 2, (FFpropquery[]) { - {themeStart, &cursorTheme}, - {sizeStart, &cursorSize} - })) { - - if(cursorTheme.length == 0) - ffStrbufAppendS(&cursorTheme, themeDefault); - - if(cursorSize.length == 0) - ffStrbufAppendS(&cursorSize, sizeDefault); - } - - if(cursorTheme.length == 0) - ffPrintError(instance, FF_CURSOR_MODULE_NAME, 0, &instance->config.cursor, "Couldn't find cursor in %s", relativeFilePath); - else - printCursor(instance, &cursorTheme, &cursorSize); - - ffStrbufDestroy(&cursorTheme); - ffStrbufDestroy(&cursorSize); -} - -static bool printCursorFromXResources(FFinstance* instance) -{ - FFstrbuf theme; - ffStrbufInit(&theme); - - FFstrbuf size; - ffStrbufInit(&size); - - ffParsePropFileHomeValues(instance, ".Xresources", 2, (FFpropquery[]) { - {"Xcursor.theme :", &theme}, - {"Xcursor.size :", &size} - }); - - if(theme.length == 0) - { - ffStrbufDestroy(&size); - ffStrbufDestroy(&theme); - return false; - } - - printCursor(instance, &theme, &size); - ffStrbufDestroy(&size); - ffStrbufDestroy(&theme); - return true; -} - -static bool printCursorFromXDG(FFinstance* instance, bool user) -{ - FFstrbuf theme; - ffStrbufInit(&theme); - - if(user) - ffParsePropFileHome(instance, ".icons/default/index.theme", "Inherits =", &theme); - else - ffParsePropFile(FASTFETCH_TARGET_DIR_USR"/share/icons/default/index.theme", "Inherits =", &theme); - - if(theme.length == 0) - { - ffStrbufDestroy(&theme); - return false; - } - - printCursor(instance, &theme, NULL); - ffStrbufDestroy(&theme); - return true; -} - -static bool printCursorFromEnv(FFinstance* instance) -{ - const char* xcursor_theme = getenv("XCURSOR_THEME"); - - if(!ffStrSet(xcursor_theme)) - return false; - - FFstrbuf theme; - ffStrbufInit(&theme); - ffStrbufAppendS(&theme, xcursor_theme); - - FFstrbuf size; - ffStrbufInit(&size); - ffStrbufAppendS(&size, getenv("XCURSOR_SIZE")); - - printCursor(instance, &theme, &size); - - ffStrbufDestroy(&size); - ffStrbufDestroy(&theme); - return true; -} - -#endif void ffPrintCursor(FFinstance* instance) { - #if defined(__ANDROID__) || defined(__APPLE__) + FFCursorResult result; + ffStrbufInit(&result.error); + ffStrbufInit(&result.theme); + ffStrbufInit(&result.size); - FF_UNUSED(instance); - ffPrintError(instance, FF_CURSOR_MODULE_NAME, 0, &instance->config.cursor, "Cursor detection is not supported"); - return; + ffDetectCursor(instance, &result); - #else + if(result.error.length) + ffPrintError(instance, FF_CURSOR_MODULE_NAME, 0, &instance->config.cursor, "%s", result.error.chars); + else + printCursor(instance, &result); - const FFDisplayServerResult* wmde = ffConnectDisplayServer(instance); - - if(ffStrbufCompS(&wmde->wmPrettyName, "WSLg") == 0) - { - ffPrintError(instance, FF_CURSOR_MODULE_NAME, 0, &instance->config.cursor, "WSLg uses native windows cursor"); - return; - } - - if(ffStrbufIgnCaseCompS(&wmde->wmProtocolName, "TTY") == 0) - { - ffPrintError(instance, FF_CURSOR_MODULE_NAME, 0, &instance->config.cursor, "Cursor isn't supported in TTY"); - return; - } - - if(ffStrbufIgnCaseCompS(&wmde->dePrettyName, "KDE Plasma") == 0) - { - printCursorFromConfigFile(instance, "kcminputrc", "cursorTheme =", "Breeze", "cursorSize =", "24"); - return; - } - - if(ffStrbufStartsWithIgnCaseS(&wmde->dePrettyName, "XFCE")) - { - printCursorXFCE(instance); - return; - } - - if(ffStrbufStartsWithIgnCaseS(&wmde->dePrettyName, "LXQt")) - { - printCursorFromConfigFile(instance, "lxqt/session.conf", "cursor_theme =", "Adwaita", "cursor_size =", "24"); - return; - } - - if(ffStrbufIgnCaseCompS(&wmde->dePrettyName, "Gnome") == 0 || ffStrbufIgnCaseCompS(&wmde->dePrettyName, "Cinnamon") == 0 || ffStrbufIgnCaseCompS(&wmde->dePrettyName, "Mate") == 0) - { - printCursorGTK(instance); - return; - } - - if( - printCursorFromEnv(instance) || - printCursorFromXDG(instance, true) || - printCursorFromXResources(instance) || - printCursorFromXDG(instance, false) - ) return; - - printCursorGTK(instance); - - #endif + ffStrbufDestroy(&result.error); + ffStrbufDestroy(&result.theme); + ffStrbufDestroy(&result.size); } diff --git a/src/modules/custom.c b/src/modules/custom.c index 723f7059c..f91a5b69e 100644 --- a/src/modules/custom.c +++ b/src/modules/custom.c @@ -1,5 +1,6 @@ #include "fastfetch.h" #include "common/printing.h" +#include "util/textModifier.h" void ffPrintCustom(FFinstance* instance, const char* key, const char* value) { diff --git a/src/modules/disk.c b/src/modules/disk.c index ea92f7e89..eb6baeb47 100644 --- a/src/modules/disk.c +++ b/src/modules/disk.c @@ -1,118 +1,155 @@ #include "fastfetch.h" #include "common/printing.h" #include "common/parsing.h" +#include "common/bar.h" #include "detection/disk/disk.h" -#include - #define FF_DISK_MODULE_NAME "Disk" -#define FF_DISK_NUM_FORMAT_ARGS 4 +#define FF_DISK_NUM_FORMAT_ARGS 10 -static void createKey(FFinstance* instance, const char* folderPath, FFstrbuf* key) +static void printDisk(FFinstance* instance, const FFDisk* disk) { + FFstrbuf key; + ffStrbufInit(&key); + if(instance->config.disk.key.length == 0) { - ffStrbufAppendS(key, FF_DISK_MODULE_NAME); - if(folderPath != NULL) - ffStrbufAppendF(key, " (%s)", folderPath); + ffStrbufAppendF(&key, "%s (%s)", FF_DISK_MODULE_NAME, disk->mountpoint.chars); } else { - ffParseFormatString(key, &instance->config.disk.key, 1, (FFformatarg[]){ - {FF_FORMAT_ARG_TYPE_STRING, folderPath} + ffParseFormatString(&key, &instance->config.disk.key, 1, (FFformatarg[]){ + {FF_FORMAT_ARG_TYPE_STRBUF, &disk->mountpoint} }); } -} - -static void printStatvfs(FFinstance* instance, const FFstrbuf* key, const char* folderPath, struct statvfs* fs) -{ - uint64_t total = fs->f_blocks * fs->f_frsize; - - if(total == 0) - { - ffPrintErrorString(instance, key->chars, 0, NULL, &instance->config.disk.errorFormat, "statvfs for %s returned size 0", folderPath); - return; - } - - uint64_t used = total - (fs->f_bavail * fs->f_frsize); - uint32_t files = (uint32_t) (fs->f_files - fs->f_ffree); - uint8_t percentage = (uint8_t) ((used / (long double) total) * 100.0); FFstrbuf usedPretty; ffStrbufInit(&usedPretty); - ffParseSize(used, instance->config.binaryPrefixType, &usedPretty); + ffParseSize(disk->bytesUsed, instance->config.binaryPrefixType, &usedPretty); FFstrbuf totalPretty; ffStrbufInit(&totalPretty); - ffParseSize(total, instance->config.binaryPrefixType, &totalPretty); + ffParseSize(disk->bytesTotal, instance->config.binaryPrefixType, &totalPretty); + + uint8_t bytesPercentage = disk->bytesTotal > 0 ? (uint8_t) (((long double) disk->bytesUsed / (long double) disk->bytesTotal) * 100.0) : 0; if(instance->config.disk.outputFormat.length == 0) { - ffPrintLogoAndKey(instance, key->chars, 0, NULL); - printf("%s / %s (%u%%)\n", usedPretty.chars, totalPretty.chars, percentage); + ffPrintLogoAndKey(instance, key.chars, 0, NULL); + + FFstrbuf str; + ffStrbufInit(&str); + + if(disk->bytesTotal > 0) + { + if(instance->config.percentType & FF_PERCENTAGE_TYPE_BAR_BIT) + { + ffAppendPercentBar(instance, &str, bytesPercentage, 0, 5, 8); + ffStrbufAppendC(&str, ' '); + } + ffStrbufAppendF(&str, "%s / %s", usedPretty.chars, totalPretty.chars); + + if(instance->config.percentType & FF_PERCENTAGE_TYPE_NUM_BIT) + ffStrbufAppendF(&str, " (%u%%)", bytesPercentage); + } + else + ffStrbufAppendS(&str, "Unknown"); + + if(disk->type == FF_DISK_TYPE_EXTERNAL) + ffStrbufAppendS(&str, " [Removable]"); + + ffStrbufPutTo(&str, stdout); + ffStrbufDestroy(&str); } else { - ffPrintFormatString(instance, key->chars, 0, NULL, &instance->config.disk.outputFormat, FF_DISK_NUM_FORMAT_ARGS, (FFformatarg[]){ + uint8_t filesPercentage = disk->filesTotal > 0 ? (uint8_t) (((double) disk->filesUsed / (double) disk->filesTotal) * 100.0) : 0; + + ffPrintFormatString(instance, key.chars, 0, NULL, &instance->config.disk.outputFormat, FF_DISK_NUM_FORMAT_ARGS, (FFformatarg[]){ {FF_FORMAT_ARG_TYPE_STRBUF, &usedPretty}, {FF_FORMAT_ARG_TYPE_STRBUF, &totalPretty}, - {FF_FORMAT_ARG_TYPE_UINT8, &percentage}, - {FF_FORMAT_ARG_TYPE_UINT, &files}, + {FF_FORMAT_ARG_TYPE_UINT8, &bytesPercentage}, + {FF_FORMAT_ARG_TYPE_UINT, &disk->filesUsed}, + {FF_FORMAT_ARG_TYPE_UINT, &disk->filesTotal}, + {FF_FORMAT_ARG_TYPE_UINT8, &filesPercentage}, + {FF_FORMAT_ARG_TYPE_BOOL, FF_FORMAT_ARG_VALUE_BOOL(disk->type == FF_DISK_TYPE_EXTERNAL)}, + {FF_FORMAT_ARG_TYPE_BOOL, FF_FORMAT_ARG_VALUE_BOOL(disk->type == FF_DISK_TYPE_HIDDEN)}, + {FF_FORMAT_ARG_TYPE_STRBUF, &disk->filesystem}, + {FF_FORMAT_ARG_TYPE_STRBUF, &disk->name} }); } ffStrbufDestroy(&totalPretty); ffStrbufDestroy(&usedPretty); -} - -static void printFolder(FFinstance* instance, const char* folderPath) -{ - FFstrbuf key; - ffStrbufInit(&key); - createKey(instance, folderPath, &key); - - struct statvfs fs; - int ret = statvfs(folderPath, &fs); - if(ret != 0) - { - ffPrintErrorString(instance, key.chars, 0, NULL, &instance->config.disk.errorFormat, "statvfs(\"%s\", &fs) != 0 (%i)", folderPath, ret); - ffStrbufDestroy(&key); - return; - } - - printStatvfs(instance, &key, folderPath, &fs); ffStrbufDestroy(&key); } -void ffPrintDisk(FFinstance* instance) +static void printMountpoint(FFinstance* instance, const FFlist* disks, const char* mountpoint) { - ffStrbufTrim(&instance->config.diskFolders, ':'); - - if(instance->config.diskFolders.length == 0) + for(uint32_t i = disks->length; i > 0; i--) { - FFlist folders; - ffListInit(&folders, sizeof(FFstrbuf)); - const char* error = ffDiskAutodetectFolders(instance, &folders); - if(error) - ffPrintError(instance, FF_DISK_MODULE_NAME, 0, &instance->config.disk, "%s", error); - for(uint32_t i = 0; i < folders.length; ++i) + FFDisk* disk = ffListGet(disks, i - 1); + if(strncmp(mountpoint, disk->mountpoint.chars, disk->mountpoint.length) == 0) { - FFstrbuf* folder = (FFstrbuf*)ffListGet(&folders, i); - printFolder(instance, folder->chars); - ffStrbufDestroy(folder); + printDisk(instance, disk); + return; } - ffListDestroy(&folders); - return; } - uint32_t startIndex = 0; - while(startIndex < instance->config.diskFolders.length) - { - uint32_t colonIndex = ffStrbufNextIndexC(&instance->config.diskFolders, startIndex, ':'); - instance->config.diskFolders.chars[colonIndex] = '\0'; + ffPrintError(instance, FF_DISK_MODULE_NAME, 0, &instance->config.disk, "No disk found for mountpoint: %s", mountpoint); +} - printFolder(instance, instance->config.diskFolders.chars + startIndex); +static void printMountpoints(FFinstance* instance, const FFlist* disks) +{ + #ifdef _WIN32 + const char separator = ';'; + #else + const char separator = ':'; + #endif + + FFstrbuf mountpoints; + ffStrbufInitCopy(&mountpoints, &instance->config.diskFolders); + ffStrbufTrim(&mountpoints, separator); + + uint32_t startIndex = 0; + while(startIndex < mountpoints.length) + { + uint32_t colonIndex = ffStrbufNextIndexC(&mountpoints, startIndex, separator); + mountpoints.chars[colonIndex] = '\0'; + + printMountpoint(instance, disks, mountpoints.chars + startIndex); startIndex = colonIndex + 1; } } + +static void printAutodetected(FFinstance* instance, const FFlist* disks) +{ + for(uint32_t i = 0; i < disks->length; i++) + { + const FFDisk* disk = ffListGet(disks, i); + + if(disk->type == FF_DISK_TYPE_EXTERNAL && !instance->config.diskShowRemovable) + continue; + + if(disk->type == FF_DISK_TYPE_HIDDEN && !instance->config.diskShowHidden) + continue; + + printDisk(instance, disk); + } +} + +void ffPrintDisk(FFinstance* instance) +{ + const FFDiskResult* disks = ffDetectDisks(); + if(disks->error.length > 0) + { + ffPrintError(instance, FF_DISK_MODULE_NAME, 0, &instance->config.disk, "%s", disks->error.chars); + return; + } + + if(instance->config.diskFolders.length == 0) + printAutodetected(instance, &disks->disks); + else + printMountpoints(instance, &disks->disks); +} diff --git a/src/modules/font.c b/src/modules/font.c index bcf8fd14d..25f394f61 100644 --- a/src/modules/font.c +++ b/src/modules/font.c @@ -23,6 +23,7 @@ static void printFont(const FFFontResult* font) } ffStrbufWriteTo(>k, stdout); + ffStrbufDestroy(>k); } #elif defined(__APPLE__) @@ -40,6 +41,21 @@ static void printFont(const FFFontResult* font) printf("%s [User]", font->fonts[1].chars); } +#elif defined(_WIN32) + +static void printFont(const FFFontResult* font) +{ + const char* types[] = { "Caption", "Menu", "Message", "Status" }; + for(uint32_t i = 0; i < sizeof(types) / sizeof(types[0]); ++i) + { + if(font->fonts[i].length > 0) + { + printf("%s [%s]", font->fonts[i].chars, types[i]); + break; + } + } +} + #else static void printFont(const FFFontResult* font) diff --git a/src/modules/gpu.c b/src/modules/gpu.c index c98ca3af8..0d0ae8266 100644 --- a/src/modules/gpu.c +++ b/src/modules/gpu.c @@ -1,6 +1,5 @@ #include "fastfetch.h" #include "common/printing.h" -#include "common/caching.h" #include "detection/host/host.h" #include "detection/gpu/gpu.h" @@ -9,47 +8,47 @@ #define FF_GPU_MODULE_NAME "GPU" #define FF_GPU_NUM_FORMAT_ARGS 5 -static void printGPUResult(FFinstance* instance, uint8_t index, FFcache* cache, FFGPUResult* gpu) +static void printGPUResult(FFinstance* instance, uint8_t index, FFGPUResult* gpu) { - FFstrbuf output; - ffStrbufInitA(&output, gpu->vendor.length + 1 + gpu->name.length); - - if(gpu->vendor.length > 0) + if(instance->config.gpu.outputFormat.length == 0) { - ffStrbufAppend(&output, &gpu->vendor); - ffStrbufAppendC(&output, ' '); + ffPrintLogoAndKey(instance, FF_GPU_MODULE_NAME, 0, &instance->config.gpu.key); + + FFstrbuf output; + ffStrbufInitA(&output, gpu->vendor.length + 1 + gpu->name.length); + + if(gpu->vendor.length > 0 && !ffStrbufStartsWith(&gpu->name, &gpu->vendor)) + { + ffStrbufAppend(&output, &gpu->vendor); + ffStrbufAppendC(&output, ' '); + } + + ffStrbufAppend(&output, &gpu->name); + + if(gpu->coreCount != FF_GPU_CORE_COUNT_UNSET) + ffStrbufAppendF(&output, " (%d)", gpu->coreCount); + + if(gpu->temperature == gpu->temperature) //FF_GPU_TEMP_UNSET + ffStrbufAppendF(&output, " - %.1f°C", gpu->temperature); + + ffStrbufPutTo(&output, stdout); + + ffStrbufDestroy(&output); + } + else + { + ffPrintFormat(instance, FF_GPU_MODULE_NAME, index, &instance->config.gpu, FF_GPU_NUM_FORMAT_ARGS, (FFformatarg[]){ + {FF_FORMAT_ARG_TYPE_STRBUF, &gpu->vendor}, + {FF_FORMAT_ARG_TYPE_STRBUF, &gpu->name}, + {FF_FORMAT_ARG_TYPE_STRBUF, &gpu->driver}, + {FF_FORMAT_ARG_TYPE_DOUBLE, &gpu->temperature}, + {FF_FORMAT_ARG_TYPE_INT, &gpu->coreCount}, + }); } - - ffStrbufAppend(&output, &gpu->name); - - if(gpu->coreCount != FF_GPU_CORE_COUNT_UNSET) - ffStrbufAppendF(&output, " (%d)", gpu->coreCount); - - if(gpu->temperature == gpu->temperature) //FF_GPU_TEMP_UNSET - ffStrbufAppendF(&output, " - %.1f°C", gpu->temperature); - - ffPrintAndAppendToCache(instance, FF_GPU_MODULE_NAME, index, &instance->config.gpu, cache, &output, FF_GPU_NUM_FORMAT_ARGS, (FFformatarg[]){ - {FF_FORMAT_ARG_TYPE_STRBUF, &gpu->vendor}, - {FF_FORMAT_ARG_TYPE_STRBUF, &gpu->name}, - {FF_FORMAT_ARG_TYPE_STRBUF, &gpu->driver}, - {FF_FORMAT_ARG_TYPE_DOUBLE, &gpu->temperature}, //FIXME: temperature shouldn't be cached - {FF_FORMAT_ARG_TYPE_INT, &gpu->coreCount}, - }); - - ffStrbufDestroy(&output); } void ffPrintGPU(FFinstance* instance) { - if(ffPrintFromCache(instance, FF_GPU_MODULE_NAME, &instance->config.gpu, FF_GPU_NUM_FORMAT_ARGS)) - return; - - if(ffStrbufCompS(&ffDetectHost()->productName, FF_HOST_PRODUCT_NAME_WSL) == 0) - { - ffPrintError(instance, FF_GPU_MODULE_NAME, 0, &instance->config.gpu, "WSL doesn't expose senseful GPU names"); - return; - } - const FFlist* gpus = ffDetectGPU(instance); if(gpus->length == 0) @@ -58,11 +57,6 @@ void ffPrintGPU(FFinstance* instance) return; } - FFcache cache; - ffCacheOpenWrite(instance, FF_GPU_MODULE_NAME, &cache); - for(uint8_t i = 0; i < (uint8_t) gpus->length; i++) - printGPUResult(instance, gpus->length == 1 ? 0 : (uint8_t) (i + 1), &cache, ffListGet(gpus, i)); - - ffCacheClose(&cache); + printGPUResult(instance, gpus->length == 1 ? 0 : (uint8_t) (i + 1), ffListGet(gpus, i)); } diff --git a/src/modules/host.c b/src/modules/host.c index 1a5b8dae6..e8d8e6044 100644 --- a/src/modules/host.c +++ b/src/modules/host.c @@ -1,55 +1,55 @@ #include "fastfetch.h" #include "common/printing.h" -#include "common/caching.h" #include "detection/host/host.h" #define FF_HOST_MODULE_NAME "Host" -#define FF_HOST_NUM_FORMAT_ARGS 15 +#define FF_HOST_NUM_FORMAT_ARGS 5 void ffPrintHost(FFinstance* instance) { - if(ffPrintFromCache(instance, FF_HOST_MODULE_NAME, &instance->config.host, FF_HOST_NUM_FORMAT_ARGS)) - return; - const FFHostResult* host = ffDetectHost(); + if(host->error.length > 0) + { + ffPrintError(instance, FF_HOST_MODULE_NAME, 0, &instance->config.host, "%*s", host->error.length, host->error.chars); + return; + } + if(host->productFamily.length == 0 && host->productName.length == 0) { ffPrintError(instance, FF_HOST_MODULE_NAME, 0, &instance->config.host, "neither product_family nor product_name is set by O.E.M."); return; } - FFstrbuf output; - ffStrbufInit(&output); - - if(host->productName.length > 0) - ffStrbufAppend(&output, &host->productName); - else - ffStrbufAppend(&output, &host->productFamily); - - if(host->productVersion.length > 0) + if(instance->config.host.outputFormat.length == 0) { - ffStrbufAppendC(&output, ' '); - ffStrbufAppend(&output, &host->productVersion); + ffPrintLogoAndKey(instance, FF_HOST_MODULE_NAME, 0, &instance->config.host.key); + + FFstrbuf output; + ffStrbufInit(&output); + + if(host->productName.length > 0) + ffStrbufAppend(&output, &host->productName); + else + ffStrbufAppend(&output, &host->productFamily); + + if(host->productVersion.length > 0) + { + ffStrbufAppendF(&output, " (%s)", host->productVersion.chars); + } + + ffStrbufPutTo(&output, stdout); + + ffStrbufDestroy(&output); + } + else + { + ffPrintFormat(instance, FF_HOST_MODULE_NAME, 0, &instance->config.host, FF_HOST_NUM_FORMAT_ARGS, (FFformatarg[]) { + {FF_FORMAT_ARG_TYPE_STRBUF, &host->productFamily}, + {FF_FORMAT_ARG_TYPE_STRBUF, &host->productName}, + {FF_FORMAT_ARG_TYPE_STRBUF, &host->productVersion}, + {FF_FORMAT_ARG_TYPE_STRBUF, &host->productSku}, + {FF_FORMAT_ARG_TYPE_STRBUF, &host->sysVendor} + }); } - - ffPrintAndWriteToCache(instance, FF_HOST_MODULE_NAME, &instance->config.host, &output, FF_HOST_NUM_FORMAT_ARGS, (FFformatarg[]) { - {FF_FORMAT_ARG_TYPE_STRBUF, &host->productFamily}, - {FF_FORMAT_ARG_TYPE_STRBUF, &host->productName}, - {FF_FORMAT_ARG_TYPE_STRBUF, &host->productVersion}, - {FF_FORMAT_ARG_TYPE_STRBUF, &host->productSku}, - {FF_FORMAT_ARG_TYPE_STRBUF, &host->biosDate}, - {FF_FORMAT_ARG_TYPE_STRBUF, &host->biosRelease}, - {FF_FORMAT_ARG_TYPE_STRBUF, &host->biosVendor}, - {FF_FORMAT_ARG_TYPE_STRBUF, &host->biosVersion}, - {FF_FORMAT_ARG_TYPE_STRBUF, &host->boardName}, - {FF_FORMAT_ARG_TYPE_STRBUF, &host->boardVendor}, - {FF_FORMAT_ARG_TYPE_STRBUF, &host->boardVersion}, - {FF_FORMAT_ARG_TYPE_STRBUF, &host->chassisType}, - {FF_FORMAT_ARG_TYPE_STRBUF, &host->chassisVendor}, - {FF_FORMAT_ARG_TYPE_STRBUF, &host->chassisVersion}, - {FF_FORMAT_ARG_TYPE_STRBUF, &host->sysVendor} - }); - - ffStrbufDestroy(&output); } diff --git a/src/modules/icons.c b/src/modules/icons.c index 38d0823fb..4597cf2e8 100644 --- a/src/modules/icons.c +++ b/src/modules/icons.c @@ -10,7 +10,7 @@ void ffPrintIcons(FFinstance* instance) { - #if defined(__ANDROID__) || defined(__APPLE__) + #if defined(__ANDROID__) || defined(__APPLE__) || defined(_WIN32) FF_UNUSED(instance); ffPrintError(instance, FF_ICONS_MODULE_NAME, 0, &instance->config.icons, "Icons detection is not supported"); diff --git a/src/modules/kernel.c b/src/modules/kernel.c index 062b04f42..72096ce55 100644 --- a/src/modules/kernel.c +++ b/src/modules/kernel.c @@ -9,7 +9,15 @@ void ffPrintKernel(FFinstance* instance) if(instance->config.kernel.outputFormat.length == 0) { ffPrintLogoAndKey(instance, FF_KERNEL_MODULE_NAME, 0, &instance->config.kernel.key); - puts(instance->state.utsname.release); + + #ifdef _WIN32 + if(instance->state.utsname.version[0] != '\0') + printf("%s (%s)\n", instance->state.utsname.release, instance->state.utsname.version); + else + puts(instance->state.utsname.release); + #else + puts(instance->state.utsname.release); + #endif } else { diff --git a/src/modules/locale.c b/src/modules/locale.c index 56ec93831..8c56b0638 100644 --- a/src/modules/locale.c +++ b/src/modules/locale.c @@ -1,75 +1,33 @@ #include "fastfetch.h" -#include "common/properties.h" #include "common/printing.h" -#include "common/caching.h" -#include "common/processing.h" -#include "common/parsing.h" - -#include -#include +#include "detection/locale/locale.h" #define FF_LOCALE_MODULE_NAME "Locale" #define FF_LOCALE_NUM_FORMAT_ARGS 1 -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 getLocaleFromCmd(FFstrbuf* locale) -{ - ffStrbufAppendS(locale, setlocale(LC_ALL, NULL)); - if(locale->length > 0) - return; - - ffStrbufAppendS(locale, setlocale(LC_MESSAGES, NULL)); -} - void ffPrintLocale(FFinstance* instance) { - if(ffPrintFromCache(instance, FF_LOCALE_MODULE_NAME, &instance->config.locale, FF_LOCALE_NUM_FORMAT_ARGS)) - return; - - FFstrbuf locale; + FFstrbuf locale; ffStrbufInit(&locale); - //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 =", &locale); - - if(locale.length == 0) - { - ffParsePropFile(FASTFETCH_TARGET_DIR_ETC"/locale.conf", "LANG =", &locale); - } - - if(locale.length == 0) - { - getLocaleFromEnv(&locale); - } - - if(locale.length == 0) - { - getLocaleFromCmd(&locale); - } - + ffDetectLocale(&locale); if(locale.length == 0) { ffPrintError(instance, FF_LOCALE_MODULE_NAME, 0, &instance->config.locale, "No locale found"); return; } - ffPrintAndWriteToCache(instance, FF_LOCALE_MODULE_NAME, &instance->config.locale, &locale, FF_LOCALE_NUM_FORMAT_ARGS, (FFformatarg[]){ - {FF_FORMAT_ARG_TYPE_STRBUF, &locale} - }); + if(instance->config.locale.outputFormat.length == 0) + { + ffPrintLogoAndKey(instance, FF_LOCALE_MODULE_NAME, 0, &instance->config.locale.key); + ffStrbufPutTo(&locale, stdout); + } + else + { + ffPrintFormat(instance, FF_LOCALE_MODULE_NAME, 0, &instance->config.locale, FF_LOCALE_NUM_FORMAT_ARGS, (FFformatarg[]){ + {FF_FORMAT_ARG_TYPE_STRBUF, &locale} + }); + } - ffStrbufDestroy(&locale); + ffStrbufDestroy(&locale); } diff --git a/src/modules/localip.c b/src/modules/localip.c index 9308d246a..89df09962 100644 --- a/src/modules/localip.c +++ b/src/modules/localip.c @@ -1,90 +1,69 @@ #include "fastfetch.h" #include "common/printing.h" +#include "detection/localip/localip.h" #define FF_LOCALIP_MODULE_NAME "Local IP" -#define FF_LOCALIP_NUM_FORMAT_ARGS 1 - -#include -#include -#include -#include -#include - -#ifdef __FreeBSD__ - #include // FreeBSD needs this for AF_INET -#endif - -static void printValue(FFinstance* instance, const char* ifaName, const char* addressBuffer) -{ - FFstrbuf key; - ffStrbufInit(&key); - - if (instance->config.localIP.key.length == 0) - { - ffStrbufAppendF(&key, FF_LOCALIP_MODULE_NAME " (%s)", ifaName); - } - else - { - ffParseFormatString(&key, &instance->config.localIP.key, 1, (FFformatarg[]){ - {FF_FORMAT_ARG_TYPE_STRING, ifaName} - }); - } - - if (instance->config.localIP.outputFormat.length == 0) - { - ffPrintLogoAndKey(instance, key.chars, 0, NULL); - puts(addressBuffer); - } - else - { - ffPrintFormatString(instance, key.chars, 0, NULL, &instance->config.localIP.outputFormat, FF_LOCALIP_NUM_FORMAT_ARGS, (FFformatarg[]){ - {FF_FORMAT_ARG_TYPE_STRING, addressBuffer} - }); - } - - ffStrbufDestroy(&key); -} +#define FF_LOCALIP_NUM_FORMAT_ARGS 2 void ffPrintLocalIp(FFinstance* instance) { - struct ifaddrs* ifAddrStruct = NULL; - int ret = getifaddrs(&ifAddrStruct); - if (ret < 0) { - ffPrintError(instance, FF_LOCALIP_MODULE_NAME, 0, &instance->config.localIP, "getifaddrs(&ifAddrStruct) < 0 (%i)", ret); - return; + FFlist results; + ffListInit(&results, sizeof(FFLocalIpResult)); + + const char* error = ffDetectLocalIps(instance, &results); + + if(error) + { + ffPrintError(instance, FF_LOCALIP_MODULE_NAME, 0, &instance->config.localIP, "%s", error); + goto exit; } - for (struct ifaddrs* ifa = ifAddrStruct; ifa != NULL; ifa = ifa->ifa_next) { - if (!ifa->ifa_addr) - continue; + if(results.length == 0) + { + ffPrintError(instance, FF_LOCALIP_MODULE_NAME, 0, &instance->config.localIP, "Failed to detect any IPs"); + goto exit; + } - // loop back - if (strncmp(ifa->ifa_name, "lo", 2) == 0 && (ifa->ifa_name[2] == '\0' || isdigit(ifa->ifa_name[2])) && !instance->config.localIpShowLoop) - continue; + FFstrbuf key; + ffStrbufInit(&key); - if (instance->config.localIpNamePrefix.length && strncmp(ifa->ifa_name, instance->config.localIpNamePrefix.chars, instance->config.localIpNamePrefix.length) != 0) - continue; + for(uint32_t i = 0; i < results.length; ++i) + { + FFLocalIpResult* ip = (FFLocalIpResult*) ffListGet(&results, i); - if (ifa->ifa_addr->sa_family == AF_INET) { // check it is IP4 - // is a valid IP4 Address - if (!instance->config.localIpShowIpV4) - continue; - - void* tmpAddrPtr=&((struct sockaddr_in *)ifa->ifa_addr)->sin_addr; - char addressBuffer[INET_ADDRSTRLEN]; - inet_ntop(AF_INET, tmpAddrPtr, addressBuffer, INET_ADDRSTRLEN); - printValue(instance, ifa->ifa_name, addressBuffer); - } else if (ifa->ifa_addr->sa_family == AF_INET6) { // check it is IP6 - // is a valid IP6 Address - if (!instance->config.localIpShowIpV6) - continue; - - void* tmpAddrPtr=&((struct sockaddr_in6 *)ifa->ifa_addr)->sin6_addr; - char addressBuffer[INET6_ADDRSTRLEN]; - inet_ntop(AF_INET6, tmpAddrPtr, addressBuffer, INET6_ADDRSTRLEN); - printValue(instance, ifa->ifa_name, addressBuffer); + if(instance->config.localIP.key.length == 0) + { + if(ip->name.length) + ffStrbufSetF(&key, FF_LOCALIP_MODULE_NAME " (%*s)", ip->name.length, ip->name.chars); + else + ffStrbufSetS(&key, FF_LOCALIP_MODULE_NAME); } + else + { + ffParseFormatString(&key, &instance->config.localIP.key, 1, (FFformatarg[]){ + {FF_FORMAT_ARG_TYPE_STRBUF, &ip->name} + }); + } + + if(instance->config.localIP.outputFormat.length == 0) + { + ffPrintLogoAndKey(instance, key.chars, 0, NULL); + ffStrbufPutTo(&ip->addr, stdout); + } + else + { + ffPrintFormatString(instance, key.chars, 0, NULL, &instance->config.localIP.outputFormat, FF_LOCALIP_NUM_FORMAT_ARGS, (FFformatarg[]){ + {FF_FORMAT_ARG_TYPE_STRBUF, &ip->addr}, + {FF_FORMAT_ARG_TYPE_STRING, ip->ipv6 ? "IPv6" : "IPv4"} + }); + } + + ffStrbufDestroy(&ip->name); + ffStrbufDestroy(&ip->addr); } - if (ifAddrStruct) freeifaddrs(ifAddrStruct); + ffStrbufDestroy(&key); + +exit: + ffListDestroy(&results); } diff --git a/src/modules/memory.c b/src/modules/memory.c index f3f2c17ee..149eef45b 100644 --- a/src/modules/memory.c +++ b/src/modules/memory.c @@ -1,13 +1,23 @@ #include "fastfetch.h" #include "common/printing.h" #include "common/parsing.h" +#include "common/bar.h" #include "detection/memory/memory.h" +#include "detection/swap/swap.h" #define FF_MEMORY_MODULE_NAME "Memory" #define FF_SWAP_MODULE_NAME "Swap" #define FF_MEMORY_NUM_FORMAT_ARGS 3 +static uint8_t calculatePercentage(const FFMemoryStorage* storage) +{ + if(storage->error.length != 0 || storage->bytesTotal == 0) + return 0; + else + return (uint8_t) (((long double) storage->bytesUsed / (long double) storage->bytesTotal) * 100.0); +} + static void printMemory(FFinstance* instance, const char* name, const FFModuleArgs* moduleArgs, const FFMemoryStorage* storage) { if(storage->error.length > 0) @@ -24,20 +34,39 @@ static void printMemory(FFinstance* instance, const char* name, const FFModuleAr ffStrbufInit(&totalPretty); ffParseSize(storage->bytesTotal, instance->config.binaryPrefixType, &totalPretty); + uint8_t percentage = calculatePercentage(storage); + if(moduleArgs->outputFormat.length == 0) { ffPrintLogoAndKey(instance, name, 0, &moduleArgs->key); if (storage->bytesTotal == 0) puts("Disabled"); else - printf("%s / %s (%u%%)\n", usedPretty.chars, totalPretty.chars, storage->percentage); + { + FFstrbuf str; + ffStrbufInit(&str); + + if(instance->config.percentType & FF_PERCENTAGE_TYPE_BAR_BIT) + { + ffAppendPercentBar(instance, &str, percentage, 0, 5, 8); + ffStrbufAppendC(&str, ' '); + } + + ffStrbufAppendF(&str, "%s / %s", usedPretty.chars, totalPretty.chars); + + if(instance->config.percentType & FF_PERCENTAGE_TYPE_NUM_BIT) + ffStrbufAppendF(&str, " (%u%%)", percentage); + + ffStrbufPutTo(&str, stdout); + ffStrbufDestroy(&str); + } } else { ffPrintFormat(instance, name, 0, moduleArgs, FF_MEMORY_NUM_FORMAT_ARGS, (FFformatarg[]){ {FF_FORMAT_ARG_TYPE_STRBUF, &usedPretty}, {FF_FORMAT_ARG_TYPE_STRBUF, &totalPretty}, - {FF_FORMAT_ARG_TYPE_UINT8, &storage->percentage}, + {FF_FORMAT_ARG_TYPE_UINT8, &percentage}, }); } @@ -47,12 +76,18 @@ static void printMemory(FFinstance* instance, const char* name, const FFModuleAr void ffPrintMemory(FFinstance* instance) { - const FFMemoryResult* memory = ffDetectMemory(); - printMemory(instance, FF_MEMORY_MODULE_NAME, &instance->config.memory, &memory->ram); + FFMemoryStorage result; + ffStrbufInit(&result.error); + ffDetectMemory(&result); + printMemory(instance, FF_MEMORY_MODULE_NAME, &instance->config.memory, &result); + ffStrbufDestroy(&result.error); } void ffPrintSwap(FFinstance* instance) { - const FFMemoryResult* memory = ffDetectMemory(); - printMemory(instance, FF_SWAP_MODULE_NAME, &instance->config.swap, &memory->swap); + FFMemoryStorage result; + ffStrbufInit(&result.error); + ffDetectSwap(&result); + printMemory(instance, FF_SWAP_MODULE_NAME, &instance->config.swap, &result); + ffStrbufDestroy(&result.error); } diff --git a/src/modules/opencl.c b/src/modules/opencl.c index 7f2ce1acb..debfcd45e 100644 --- a/src/modules/opencl.c +++ b/src/modules/opencl.c @@ -83,7 +83,7 @@ static const char* printOpenCL(FFinstance* instance) { OpenCLData data; - FF_LIBRARY_LOAD(opencl, &instance->config.libOpenCL, "dlopen libOpenCL.so failed", "libOpenCL.so", 1); + FF_LIBRARY_LOAD(opencl, &instance->config.libOpenCL, "dlopen libOpenCL"FF_LIBRARY_EXTENSION" failed", "libOpenCL"FF_LIBRARY_EXTENSION, 1); FF_LIBRARY_LOAD_SYMBOL_VAR_MESSAGE(opencl, data, clGetPlatformIDs); FF_LIBRARY_LOAD_SYMBOL_VAR_MESSAGE(opencl, data, clGetDeviceIDs); FF_LIBRARY_LOAD_SYMBOL_VAR_MESSAGE(opencl, data, clGetDeviceInfo); diff --git a/src/modules/opengl.c b/src/modules/opengl.c index 78d0b4846..61e78451b 100644 --- a/src/modules/opengl.c +++ b/src/modules/opengl.c @@ -1,457 +1,42 @@ #include "fastfetch.h" #include "common/printing.h" -#include "common/parsing.h" - -#include +#include "detection/opengl/opengl.h" #define FF_OPENGL_MODULE_NAME "OpenGL" #define FF_OPENGL_NUM_FORMAT_ARGS 4 -#if defined(FF_HAVE_EGL) || defined(FF_HAVE_GLX) || defined(FF_HAVE_OSMESA) || defined(__APPLE__) -#define FF_HAVE_GL 1 - -#include "common/library.h" - -#ifdef __APPLE__ - #define GL_SILENCE_DEPRECATION - #include -#else - #include -#endif - -#define FF_OPENGL_BUFFER_WIDTH 1 -#define FF_OPENGL_BUFFER_HEIGHT 1 - -typedef struct GLData +void ffPrintOpenGL(FFinstance* instance) { - FF_LIBRARY_SYMBOL(glGetString) -} GLData; + FFOpenGLResult result; + ffStrbufInit(&result.version); + ffStrbufInit(&result.renderer); + ffStrbufInit(&result.vendor); + ffStrbufInit(&result.slv); -static const char* glHandlePrint(FFinstance* instance, const GLData* data) -{ - const char* version = (const char*) data->ffglGetString(GL_VERSION); - if(!ffStrSet(version)) - return "glGetString(GL_VERSION) returned NULL"; - - const char* renderer = (const char*) data->ffglGetString(GL_RENDERER); - const char* vendor = (const char*) data->ffglGetString(GL_VENDOR); - const char* slv = (const char*) data->ffglGetString(GL_SHADING_LANGUAGE_VERSION); + const char* error = ffDetectOpenGL(instance, &result); + if(error) + { + ffPrintError(instance, FF_OPENGL_MODULE_NAME, 0, &instance->config.openGL, "%s", error); + return; + } if(instance->config.openGL.outputFormat.length == 0) { ffPrintLogoAndKey(instance, FF_OPENGL_MODULE_NAME, 0, &instance->config.openGL.key); - puts(version); + puts(result.version.chars); } else { ffPrintFormat(instance, FF_OPENGL_MODULE_NAME, 0, &instance->config.openGL, FF_OPENGL_NUM_FORMAT_ARGS, (FFformatarg[]) { - {FF_FORMAT_ARG_TYPE_STRING, version}, - {FF_FORMAT_ARG_TYPE_STRING, renderer}, - {FF_FORMAT_ARG_TYPE_STRING, vendor}, - {FF_FORMAT_ARG_TYPE_STRING, slv} + {FF_FORMAT_ARG_TYPE_STRBUF, &result.version}, + {FF_FORMAT_ARG_TYPE_STRBUF, &result.renderer}, + {FF_FORMAT_ARG_TYPE_STRBUF, &result.vendor}, + {FF_FORMAT_ARG_TYPE_STRBUF, &result.slv} }); } - 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(FFinstance* instance, EGLData* data) -{ - if(data->ffeglMakeCurrent(data->display, data->surface, data->surface, data->context) != EGL_TRUE) - return "eglMakeCurrent returned EGL_FALSE"; - - return glHandlePrint(instance, &data->glData); -} - -static const char* eglHandleSurface(FFinstance* instance, 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(instance, data); - data->ffeglDestroyContext(data->display, data->context); - return error; -} - -static const char* eglHandleDisplay(FFinstance* instance, 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(instance, data); - data->ffeglDestroySurface(data->display, data->surface); - return error; -} - -static const char* eglHandleData(FFinstance* instance, 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(instance, data); - data->ffeglTerminate(data->display); - return error; -} - -static const char* eglPrint(FFinstance* instance) -{ - EGLData eglData; - - FF_LIBRARY_LOAD(egl, &instance->config.libEGL, "dlopen egl failed", "libEGL.so", 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(instance, &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) - - Display* display; - XVisualInfo* visualInfo; - Pixmap pixmap; - GLXPixmap glxPixmap; - GLXContext context; -} GLXData; - -static const char* glxHandleContext(FFinstance* instance, GLXData* data) -{ - if(data->ffglXMakeCurrent(data->display, data->glxPixmap, data->context) != True) - return "glXMakeCurrent returned False"; - - return glHandlePrint(instance, &data->glData); -} - -static const char* glxHandleGLXPixmap(FFinstance* instance, GLXData* data) -{ - data->context = data->ffglXCreateContext(data->display, data->visualInfo, NULL, True); - if(data->context == NULL) - return "glXCreateContext returned NULL"; - - const char* error = glxHandleContext(instance, data); - data->ffglXDestroyContext(data->display, data->context); - return error; -} - -static const char* glxHandlePixmap(FFinstance* instance, GLXData* data) -{ - data->glxPixmap = data->ffglXCreateGLXPixmap(data->display, data->visualInfo, data->pixmap); - if(data->glxPixmap == None) - return "glXCreateGLXPixmap returned None"; - - const char* error = glxHandleGLXPixmap(instance, data); - data->ffglXDestroyGLXPixmap(data->display, data->glxPixmap); - return error; -} - -static const char* glxHandleVisualInfo(FFinstance* instance, 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(instance, data); - data->ffXFreePixmap(data->display, data->pixmap); - return error; -} - -static const char* glxHandleDisplay(FFinstance* instance, GLXData* data) -{ - data->visualInfo = data->ffglXChooseVisual(data->display, DefaultScreen(data->display), (int[]){None}); - if(data->visualInfo == NULL) - return "glXChooseVisual returned NULL"; - - return glxHandleVisualInfo(instance, data); -} - -static const char* glxHandleData(FFinstance* instance, 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(instance, data); - data->ffXCloseDisplay(data->display); - return error; -} - -static const char* glxPrint(FFinstance* instance) -{ - GLXData data; - - FF_LIBRARY_LOAD(glx, &instance->config.libGLX, "dlopen glx failed", "libGLX.so", 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); - - const char* error = glxHandleData(instance, &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(FFinstance* instance, 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 glHandlePrint(instance, &data->glData); -} - -static const char* osMesaHandleData(FFinstance* instance, 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(instance, data); - data->ffOSMesaDestroyContext(data->context); - return error; -} - -static const char* osMesaPrint(FFinstance* instance) -{ - OSMesaData data; - - FF_LIBRARY_LOAD(osmesa, &instance->config.libOSMesa, "dlopen osmesa failed", "libOSMesa.so", 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(instance, &data); - dlclose(osmesa); - return error; -} - -#endif //FF_HAVE_OSMESA - -#ifdef __APPLE__ -#include // This brings in CGL, not GL - -typedef struct CGLData -{ - CGLPixelFormatObj pixelFormat; - CGLContextObj context; -} CGLData; - -static const char* cglHandleContext(FFinstance* instance, CGLData* data) -{ - if(CGLSetCurrentContext(data->context) != kCGLNoError) - return "CGLSetCurrentContext() failed"; - - GLData glData; - glData.ffglGetString = glGetString; - - return glHandlePrint(instance, &glData); -} - -static const char* cglHandlePixelFormat(FFinstance* instance, CGLData* data) -{ - if(CGLCreateContext(data->pixelFormat, NULL, &data->context) != kCGLNoError) - return "CGLCreateContext() failed"; - - const char* error = cglHandleContext(instance, data); - CGLDestroyContext(data->context); - return error; -} - -static const char* cglPrint(FFinstance* instance) -{ - CGLData data; - - CGLPixelFormatAttribute attrs[] = { - kCGLPFAOpenGLProfile, (CGLPixelFormatAttribute) kCGLOGLPVersion_3_2_Core, - kCGLPFAAccelerated, - 0 - }; - - GLint num; - if (CGLChoosePixelFormat(attrs, &data.pixelFormat, &num) != kCGLNoError) - return "CGLChoosePixelFormat() failed"; - - const char* error = cglHandlePixelFormat(instance, &data); - CGLDestroyPixelFormat(data.pixelFormat); - return error; -} - -#elif FF_HAVE_GL - -static const char* glPrint(FFinstance* instance) -{ - if(instance->config.glType == FF_GL_TYPE_GLX) - { - #ifdef FF_HAVE_GLX - return glxPrint(instance); - #else - return "fastfetch was compiled without glx support"; - #endif - } - - if(instance->config.glType == FF_GL_TYPE_EGL) - { - #ifdef FF_HAVE_EGL - return eglPrint(instance); - #else - return "fastfetch was compiled without egl support"; - #endif - } - - if(instance->config.glType == FF_GL_TYPE_OSMESA) - { - #ifdef FF_HAVE_OSMESA - return osMesaPrint(instance); - #else - return "fastfetch was compiled without osmesa support"; - #endif - } - - const char* error = ""; // not NULL dummy value - - #ifdef FF_HAVE_EGL - error = eglPrint(instance); - #endif - - #ifdef FF_HAVE_GLX - if(error != NULL) - error = glxPrint(instance); - #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; -} - -#endif // !__APPLE__ && FF_HAVE_GL - -void ffPrintOpenGL(FFinstance* instance) -{ - const char* error; - - #ifndef FF_HAVE_GL - error = "Fastfetch was built without gl support."; - #elif __APPLE__ - error = cglPrint(instance); - #else - error = glPrint(instance); - #endif - - if(error != NULL) - ffPrintError(instance, FF_OPENGL_MODULE_NAME, 0, &instance->config.openGL, "%s", error); + ffStrbufDestroy(&result.version); + ffStrbufDestroy(&result.renderer); + ffStrbufDestroy(&result.vendor); + ffStrbufDestroy(&result.slv); } diff --git a/src/modules/packages.c b/src/modules/packages.c index 62d5b9344..d22a2476f 100644 --- a/src/modules/packages.c +++ b/src/modules/packages.c @@ -1,473 +1,15 @@ #include "fastfetch.h" -#include "common/io.h" -#include "common/properties.h" #include "common/printing.h" -#include "common/settings.h" -#include "common/processing.h" -#include "common/parsing.h" -#include "detection/os/os.h" - -#include -#include -#include -#include +#include "detection/packages/packages.h" #define FF_PACKAGES_MODULE_NAME "Packages" -#define FF_PACKAGES_NUM_FORMAT_ARGS 16 - -typedef struct PackageCounts -{ - uint32_t pacman; - uint32_t dpkg; - uint32_t rpm; - uint32_t emerge; - uint32_t xbps; - uint32_t nixSystem; - uint32_t nixDefault; - uint32_t apk; - uint32_t pkg; - uint32_t flatpak; - uint32_t snap; - uint32_t brew; - uint32_t port; - - FFstrbuf pacmanBranch; -} PackageCounts; - -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; -} - -#ifndef __APPLE__ - -static uint32_t getNumStrings(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; -} - -#ifndef __ANDROID__ - -static uint32_t countFilesRecursive(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 += countFilesRecursive(baseDirPath, filename); - ffStrbufSubstrBefore(baseDirPath, baseDirPathLength); - } - - closedir(dirp); - return sum; -} - -static uint32_t getNixPackages(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 getXBPS(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 = getNumStrings(baseDir->chars, "installed"); - break; - } - - closedir(dir); - return result; -} - -#endif // !__ANDROID__ - -#else // !__APPLE__ - -static uint32_t countBrewPackages(FFstrbuf* baseDir) -{ - uint32_t result = 0; - uint32_t baseDirLength = baseDir->length; - - ffStrbufAppendS(baseDir, "/Caskroom"); - result += getNumElements(baseDir->chars, DT_DIR); - ffStrbufSubstrBefore(baseDir, baseDirLength); - - ffStrbufAppendS(baseDir, "/Cellar"); - result += getNumElements(baseDir->chars, DT_DIR); - ffStrbufSubstrBefore(baseDir, baseDirLength); - - return result; -} - -static uint32_t getBrewPackages(FFstrbuf* baseDir) -{ - uint32_t result = 0; - uint32_t baseDirLength = baseDir->length; - - const char* prefix = getenv("HOMEBREW_PREFIX"); - bool prefixSet = ffStrSet(prefix); - - if(prefixSet) - { - ffStrbufAppendS(baseDir, prefix); - result += countBrewPackages(baseDir); - ffStrbufSubstrBefore(baseDir, baseDirLength); - } - - ffStrbufAppendS(baseDir, "/opt/homebrew"); - if(!prefixSet || strcasecmp(baseDir->chars, prefix) != 0) - result += countBrewPackages(baseDir); - ffStrbufSubstrBefore(baseDir, baseDirLength); - - ffStrbufAppendS(baseDir, "/usr/local"); - if(!prefixSet || strcasecmp(baseDir->chars, prefix) != 0) - result += countBrewPackages(baseDir); - ffStrbufSubstrBefore(baseDir, baseDirLength); - - return result; -} - -static uint32_t countMacPortsPackages(FFstrbuf* baseDir) -{ - uint32_t result = 0; - uint32_t baseDirLength = baseDir->length; - - ffStrbufAppendS(baseDir, "/var/macports/software"); - result += getNumElements(baseDir->chars, DT_DIR); - ffStrbufSubstrBefore(baseDir, baseDirLength); - - return result; -} - -static uint32_t getMacPortsPackages(FFstrbuf* baseDir) -{ - uint32_t result = 0; - uint32_t baseDirLength = baseDir->length; - - const char* prefix = getenv("MACPORTS_PREFIX"); - bool prefixSet = ffStrSet(prefix); - - if(prefixSet) - { - ffStrbufAppendS(baseDir, prefix); - result += countMacPortsPackages(baseDir); - ffStrbufSubstrBefore(baseDir, baseDirLength); - } - - ffStrbufAppendS(baseDir, "/opt/local"); - if(!prefixSet || strcasecmp(baseDir->chars, prefix) != 0) - result += countMacPortsPackages(baseDir); - ffStrbufSubstrBefore(baseDir, baseDirLength); - - return result; -} - -#endif // __APPLE__ - -#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.so", 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, PackageCounts* packageCounts) -{ - #if defined(__APPLE__) || defined(__ANDROID__) - FF_UNUSED(instance); - #endif - - #ifndef __APPLE__ //Linux desktop and Android - - uint32_t baseDirLength = baseDir->length; - - //pacman - ffStrbufAppendS(baseDir, "/var/lib/pacman/local"); - packageCounts->pacman += getNumElements(baseDir->chars, DT_DIR); - ffStrbufSubstrBefore(baseDir, baseDirLength); - - //dpkg - ffStrbufAppendS(baseDir, "/var/lib/dpkg/status"); - packageCounts->dpkg += getNumStrings(baseDir->chars, "Status: "); - ffStrbufSubstrBefore(baseDir, baseDirLength); - - #ifndef __ANDROID__ //Linux Desktop - - //rpm - ffStrbufAppendS(baseDir, "/var/lib/rpm/rmpdb.sqlite"); - packageCounts->rpm += (uint32_t) ffSettingsGetSQLite3Int(instance, baseDir->chars, "SELECT count(blob) FROM Packages"); - ffStrbufSubstrBefore(baseDir, baseDirLength); - - //emerge - ffStrbufAppendS(baseDir, "/var/db/pkg"); - packageCounts->emerge += countFilesRecursive(baseDir, "SIZE"); - ffStrbufSubstrBefore(baseDir, baseDirLength); - - //xps - ffStrbufAppendS(baseDir, "/var/db/xbps"); - packageCounts->xbps += getXBPS(baseDir); - ffStrbufSubstrBefore(baseDir, baseDirLength); - - //nix system - ffStrbufAppendS(baseDir, "/run/current-system"); - packageCounts->nixSystem += getNixPackages(baseDir->chars); - ffStrbufSubstrBefore(baseDir, baseDirLength); - - //nix default - ffStrbufAppendS(baseDir, "/nix/var/nix/profiles/default"); - packageCounts->nixDefault += getNixPackages(baseDir->chars); - ffStrbufSubstrBefore(baseDir, baseDirLength); - - //apk - ffStrbufAppendS(baseDir, "/lib/apk/db/installed"); - packageCounts->apk += getNumStrings(baseDir->chars, "C:Q"); - ffStrbufSubstrBefore(baseDir, baseDirLength); - - //flatpak - ffStrbufAppendS(baseDir, "/var/lib/flatpak/app"); - packageCounts->flatpak += getNumElements(baseDir->chars, DT_DIR); - ffStrbufSubstrBefore(baseDir, baseDirLength); - - //snap - ffStrbufAppendS(baseDir, "/snap"); - uint32_t snap = getNumElements(baseDir->chars, DT_DIR); - if(snap > 0) - packageCounts->snap += (snap - 1); //Accounting for the /snap/bin folder - ffStrbufSubstrBefore(baseDir, baseDirLength); - - //pacman branch - 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); - - #endif // !__ANDROID__ - - #else // !__APPLE__ - - //brew - packageCounts->brew += getBrewPackages(baseDir); - packageCounts->port += getMacPortsPackages(baseDir); - - #endif // __APPLE__ - - #ifdef __FreeBSD__ - - ffStrbufAppendS(baseDir, "/var/db/pkg/local.sqlite"); - packageCounts->pkg += (uint32_t) ffSettingsGetSQLite3Int(instance, baseDir->chars, "SELECT count(id) FROM packages"); - ffStrbufSubstrBefore(baseDir, baseDirLength); - - #endif // __FreeBSD__ -} - -static void getPackageCountsBedrock(const FFinstance* instance, FFstrbuf* baseDir, PackageCounts* packageCounts) -{ - uint32_t baseDirLength = baseDir->length; - - ffStrbufAppendS(baseDir, "/bedrock/strata"); - - DIR* dir = opendir(baseDir->chars); - if(dir == NULL) - { - ffStrbufSubstrBefore(baseDir, baseDirLength); - getPackageCounts(instance, baseDir, packageCounts); - return; - } - - ffStrbufAppendC(baseDir, '/'); - baseDirLength = 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, baseDirLength); - } - - closedir(dir); -} +#define FF_PACKAGES_NUM_FORMAT_ARGS 20 void ffPrintPackages(FFinstance* instance) { - PackageCounts counts = {0}; - ffStrbufInit(&counts.pacmanBranch); + const FFPackagesResult* counts = ffDetectPackages(instance); + uint32_t all = counts->all; //Copy it, so we can substract from it in FF_PRINT_PACKAGE - FFstrbuf baseDir; - ffStrbufInitA(&baseDir, 512); - ffStrbufAppendS(&baseDir, FASTFETCH_TARGET_DIR_ROOT); - - if(ffStrbufIgnCaseCompS(&ffDetectOS(instance)->id, "bedrock") == 0) - getPackageCountsBedrock(instance, &baseDir, &counts); - else - getPackageCounts(instance, &baseDir, &counts); - - // 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(counts.rpm == 0) - counts.rpm = getRpmFromLibrpm(instance); - #endif - - #if !defined(__ANDROID__) && !defined(__APPLE__) - //nix user - ffStrbufSetS(&baseDir, instance->state.passwd->pw_dir); - ffStrbufAppendS(&baseDir, "/.nix-profile"); - uint32_t nixUser = getNixPackages(baseDir.chars); - #else - uint32_t nixUser = 0; - #endif - - ffStrbufDestroy(&baseDir); - - uint32_t all = counts.pacman + counts.dpkg + counts.rpm + counts.emerge + counts.xbps + counts.nixSystem + nixUser + counts.nixDefault + counts.apk + counts.pkg + counts.flatpak + counts.snap + counts.brew + counts.port; if(all == 0) { ffPrintError(instance, FF_PACKAGES_MODULE_NAME, 0, &instance->config.packages, "No packages from known package managers found"); @@ -478,84 +20,71 @@ void ffPrintPackages(FFinstance* instance) { ffPrintLogoAndKey(instance, FF_PACKAGES_MODULE_NAME, 0, &instance->config.packages.key); - #define FF_PRINT_PACKAGE(name) \ - if(counts.name > 0) \ - { \ - printf("%u ("#name")", counts.name); \ - if((all = all - counts.name) > 0) \ - printf(", "); \ - }; + #define FF_PRINT_PACKAGE_NAME(var, name) \ + if(counts->var > 0) \ + { \ + printf("%u ("name")", counts->var); \ + if((all -= counts->var) > 0) \ + printf(", "); \ + }; - if(counts.pacman > 0) + #define FF_PRINT_PACKAGE(name) FF_PRINT_PACKAGE_NAME(name, #name) + + if(counts->pacman > 0) { - printf("%u (pacman)", counts.pacman); - if(counts.pacmanBranch.length > 0) - printf("[%s]", counts.pacmanBranch.chars); - if((all = all - counts.pacman) > 0) + printf("%u (pacman)", counts->pacman); + if(counts->pacmanBranch.length > 0) + printf("[%s]", counts->pacmanBranch.chars); + if((all -= counts->pacman) > 0) printf(", "); }; FF_PRINT_PACKAGE(dpkg) FF_PRINT_PACKAGE(rpm) FF_PRINT_PACKAGE(emerge) + FF_PRINT_PACKAGE(eopkg) FF_PRINT_PACKAGE(xbps) - - if(counts.nixSystem > 0) - { - printf("%u (nix-system)", counts.nixSystem); - if((all = all - counts.nixSystem) > 0) - printf(", "); - } - - if(nixUser > 0) - { - printf("%u (nix-user)", nixUser); - if((all = all - nixUser) > 0) - printf(", "); - } - - if(counts.nixDefault > 0) - { - printf("%u (nix-default)", counts.nixDefault); - if((all = all - counts.nixDefault) > 0) - printf(", "); - } - + FF_PRINT_PACKAGE_NAME(nixSystem, "nix-system") + FF_PRINT_PACKAGE_NAME(nixUser, "nix-user") + FF_PRINT_PACKAGE_NAME(nixDefault, "nix-default") FF_PRINT_PACKAGE(apk) FF_PRINT_PACKAGE(pkg) FF_PRINT_PACKAGE(flatpak) FF_PRINT_PACKAGE(snap) FF_PRINT_PACKAGE(brew) + FF_PRINT_PACKAGE_NAME(brewCask, "brew-cask") FF_PRINT_PACKAGE(port) + FF_PRINT_PACKAGE(scoop) + FF_PRINT_PACKAGE(choco) //Fix linter warning of unused value of all (void) all; - #undef FF_PRINT_PACKAGE - putchar('\n'); } else { ffPrintFormat(instance, FF_PACKAGES_MODULE_NAME, 0, &instance->config.packages, FF_PACKAGES_NUM_FORMAT_ARGS, (FFformatarg[]){ {FF_FORMAT_ARG_TYPE_UINT, &all}, - {FF_FORMAT_ARG_TYPE_UINT, &counts.pacman}, - {FF_FORMAT_ARG_TYPE_STRBUF, &counts.pacmanBranch}, - {FF_FORMAT_ARG_TYPE_UINT, &counts.dpkg}, - {FF_FORMAT_ARG_TYPE_UINT, &counts.rpm}, - {FF_FORMAT_ARG_TYPE_UINT, &counts.emerge}, - {FF_FORMAT_ARG_TYPE_UINT, &counts.xbps}, - {FF_FORMAT_ARG_TYPE_UINT, &counts.nixSystem}, - {FF_FORMAT_ARG_TYPE_UINT, &nixUser}, - {FF_FORMAT_ARG_TYPE_UINT, &counts.nixDefault}, - {FF_FORMAT_ARG_TYPE_UINT, &counts.apk}, - {FF_FORMAT_ARG_TYPE_UINT, &counts.pkg}, - {FF_FORMAT_ARG_TYPE_UINT, &counts.flatpak}, - {FF_FORMAT_ARG_TYPE_UINT, &counts.snap}, - {FF_FORMAT_ARG_TYPE_UINT, &counts.brew}, - {FF_FORMAT_ARG_TYPE_UINT, &counts.port} + {FF_FORMAT_ARG_TYPE_UINT, &counts->pacman}, + {FF_FORMAT_ARG_TYPE_STRBUF, &counts->pacmanBranch}, + {FF_FORMAT_ARG_TYPE_UINT, &counts->dpkg}, + {FF_FORMAT_ARG_TYPE_UINT, &counts->rpm}, + {FF_FORMAT_ARG_TYPE_UINT, &counts->emerge}, + {FF_FORMAT_ARG_TYPE_UINT, &counts->eopkg}, + {FF_FORMAT_ARG_TYPE_UINT, &counts->xbps}, + {FF_FORMAT_ARG_TYPE_UINT, &counts->nixSystem}, + {FF_FORMAT_ARG_TYPE_UINT, &counts->nixUser}, + {FF_FORMAT_ARG_TYPE_UINT, &counts->nixDefault}, + {FF_FORMAT_ARG_TYPE_UINT, &counts->apk}, + {FF_FORMAT_ARG_TYPE_UINT, &counts->pkg}, + {FF_FORMAT_ARG_TYPE_UINT, &counts->flatpak}, + {FF_FORMAT_ARG_TYPE_UINT, &counts->snap}, + {FF_FORMAT_ARG_TYPE_UINT, &counts->brew}, + {FF_FORMAT_ARG_TYPE_UINT, &counts->brewCask}, + {FF_FORMAT_ARG_TYPE_UINT, &counts->port}, + {FF_FORMAT_ARG_TYPE_UINT, &counts->scoop}, + {FF_FORMAT_ARG_TYPE_UINT, &counts->choco}, }); } - - ffStrbufDestroy(&counts.pacmanBranch); } diff --git a/src/modules/processes.c b/src/modules/processes.c index d424487d8..5157f79b6 100644 --- a/src/modules/processes.c +++ b/src/modules/processes.c @@ -1,47 +1,34 @@ #include "fastfetch.h" #include "common/printing.h" +#include "detection/processes/processes.h" #define FF_PROCESSES_MODULE_NAME "Processes" #define FF_PROCESSES_NUM_FORMAT_ARGS 1 -#ifdef __APPLE__ - #include -#endif - void ffPrintProcesses(FFinstance* instance) { - #if __APPLE__ - int request[] = {CTL_KERN, KERN_PROC, KERN_PROC_ALL}; - size_t length; + FFstrbuf error; + ffStrbufInit(&error); + uint32_t numProcesses = ffDetectProcesses(&error); - if(sysctl(request, sizeof(request) / sizeof(*request), NULL, &length, NULL, 0) != 0) - { - ffPrintError(instance, FF_PROCESSES_MODULE_NAME, 0, &instance->config.processes, "sysctl() failed"); - return; - } - uint16_t numProcesses = (uint16_t)(length / sizeof(struct kinfo_proc)); - #elif FF_HAVE_SYSINFO_H - uint16_t numProcesses = (uint16_t) instance->state.sysinfo.procs; - #else - uint16_t numProcesses = 0; - #endif - - if(numProcesses == 0) + if(error.length > 0) { - ffPrintError(instance, FF_PROCESSES_MODULE_NAME, 0, &instance->config.processes, "Could not get number of processes"); + ffPrintError(instance, FF_PROCESSES_MODULE_NAME, 0, &instance->config.processes, "%*s", error.length, error.chars); + ffStrbufDestroy(&error); return; } + ffStrbufDestroy(&error); if(instance->config.processes.outputFormat.length == 0) { ffPrintLogoAndKey(instance, FF_PROCESSES_MODULE_NAME, 0, &instance->config.processes.key); - printf("%hu\n", numProcesses); + printf("%u\n", numProcesses); } else { ffPrintFormat(instance, FF_PROCESSES_MODULE_NAME, 0, &instance->config.processes, FF_PROCESSES_NUM_FORMAT_ARGS, (FFformatarg[]){ - {FF_FORMAT_ARG_TYPE_UINT16, &numProcesses} + {FF_FORMAT_ARG_TYPE_UINT, &numProcesses} }); } } diff --git a/src/modules/publicip.c b/src/modules/publicip.c index fe77e30f6..61c85698e 100644 --- a/src/modules/publicip.c +++ b/src/modules/publicip.c @@ -5,12 +5,13 @@ #define FF_PUBLICIP_MODULE_NAME "Public IP" #define FF_PUBLICIP_NUM_FORMAT_ARGS 1 -static int sockfd; +static FFNetworkingState state; +static int status = -1; void ffPreparePublicIp(FFinstance* instance) { if(instance->config.publicIpUrl.length == 0) - sockfd = ffNetworkingSendHttpRequest("ipinfo.io", "/ip", NULL, instance->config.publicIpTimeout); + status = ffNetworkingSendHttpRequest(&state, "ipinfo.io", "/ip", NULL); else { FFstrbuf host; @@ -27,7 +28,7 @@ void ffPreparePublicIp(FFinstance* instance) host.chars[pathStartIndex] = '\0'; } - sockfd = ffNetworkingSendHttpRequest(host.chars, path.length == 0 ? "/" : path.chars, NULL, instance->config.publicIpTimeout); + status = ffNetworkingSendHttpRequest(&state, host.chars, path.length == 0 ? "/" : path.chars, NULL); ffStrbufDestroy(&path); ffStrbufDestroy(&host); @@ -36,10 +37,10 @@ void ffPreparePublicIp(FFinstance* instance) void ffPrintPublicIp(FFinstance* instance) { - if(sockfd == 0) + if(status == -1) ffPreparePublicIp(instance); - if(sockfd < 0) + if(status == 0) { ffPrintError(instance, FF_PUBLICIP_MODULE_NAME, 0, &instance->config.publicIP, "Failed to connect to an IP detection server"); return; @@ -47,7 +48,7 @@ void ffPrintPublicIp(FFinstance* instance) FFstrbuf result; ffStrbufInitA(&result, 4096); - bool success = ffNetworkingRecvHttpResponse(sockfd, &result); + bool success = ffNetworkingRecvHttpResponse(&state, &result, instance->config.publicIpTimeout); if(success) ffStrbufSubstrAfterFirstS(&result, "\r\n\r\n"); if(!success || result.length == 0) diff --git a/src/modules/shell.c b/src/modules/shell.c index 3ea71f05e..607f5ec69 100644 --- a/src/modules/shell.c +++ b/src/modules/shell.c @@ -1,6 +1,6 @@ #include "fastfetch.h" #include "common/printing.h" -#include "detection/terminalshell.h" +#include "detection/terminalshell/terminalshell.h" #define FF_SHELL_MODULE_NAME "Shell" #define FF_SHELL_NUM_FORMAT_ARGS 7 @@ -18,7 +18,7 @@ void ffPrintShell(FFinstance* instance) if(instance->config.shell.outputFormat.length == 0) { ffPrintLogoAndKey(instance, FF_SHELL_MODULE_NAME, 0, &instance->config.shell.key); - fputs(result->shellExeName, stdout); + ffStrbufWriteTo(&result->shellPrettyName, stdout); if(result->shellVersion.length > 0) { diff --git a/src/modules/terminal.c b/src/modules/terminal.c index 4d930a445..469ece23b 100644 --- a/src/modules/terminal.c +++ b/src/modules/terminal.c @@ -1,6 +1,6 @@ #include "fastfetch.h" #include "common/printing.h" -#include "detection/terminalshell.h" +#include "detection/terminalshell/terminalshell.h" #include @@ -20,11 +20,7 @@ void ffPrintTerminal(FFinstance* instance) if(instance->config.terminal.outputFormat.length == 0) { ffPrintLogoAndKey(instance, FF_TERMINAL_MODULE_NAME, 0, &instance->config.terminal.key); - - if(strncmp(result->terminalExeName, result->terminalProcessName.chars, result->terminalProcessName.length) == 0) // if exeName starts with processName, print it. Otherwise print processName - puts(result->terminalExeName); - else - ffStrbufPutTo(&result->terminalProcessName, stdout); + ffStrbufPutTo(&result->terminalPrettyName, stdout); } else { diff --git a/src/modules/theme.c b/src/modules/theme.c index 612bb6b96..5c10ee485 100644 --- a/src/modules/theme.c +++ b/src/modules/theme.c @@ -10,7 +10,7 @@ void ffPrintTheme(FFinstance* instance) { - #if defined(__ANDROID__) || defined(__APPLE__) + #if defined(__ANDROID__) || defined(__APPLE__) || defined(_WIN32) FF_UNUSED(instance); ffPrintError(instance, FF_THEME_MODULE_NAME, 0, &instance->config.theme, "Theme detection is not supported"); diff --git a/src/modules/title.c b/src/modules/title.c index 089b5105c..cef00d146 100644 --- a/src/modules/title.c +++ b/src/modules/title.c @@ -1,6 +1,7 @@ #include "fastfetch.h" #include "common/printing.h" #include "detection/title.h" +#include "util/textModifier.h" static inline void printTitlePart(FFinstance* instance, const FFstrbuf* content) { diff --git a/src/modules/uptime.c b/src/modules/uptime.c index 9cbd9f9f8..48d2b4f4a 100644 --- a/src/modules/uptime.c +++ b/src/modules/uptime.c @@ -1,34 +1,13 @@ #include "fastfetch.h" #include "common/printing.h" - -#if __APPLE__ - #include "time.h" - #include "sys/sysctl.h" -#endif +#include "detection/uptime/uptime.h" #define FF_UPTIME_MODULE_NAME "Uptime" #define FF_UPTIME_NUM_FORMAT_ARGS 4 void ffPrintUptime(FFinstance* instance) { - uint64_t uptime; - - #if FF_HAVE_SYSINFO_H - uptime = (uint64_t) instance->state.sysinfo.uptime; - #elif __APPLE__ - struct timeval bootTime; - size_t bootTimeSize = sizeof(bootTime); - if(sysctl( - (int[]) {CTL_KERN, KERN_BOOTTIME}, 2, - &bootTime, &bootTimeSize, - NULL, 0 - ) == 0) - uptime = (uint64_t) difftime(time(NULL), bootTime.tv_sec); - else - uptime = 0; - #else - uptime = 0; - #endif + uint64_t uptime = ffDetectUptime(); if(uptime == 0) { diff --git a/src/modules/users.c b/src/modules/users.c index c1b85f8da..1c78571b2 100644 --- a/src/modules/users.c +++ b/src/modules/users.c @@ -1,48 +1,25 @@ #include "fastfetch.h" #include "common/printing.h" - -#if FF_HAVE_UTMPX_H - #include -#else - //for Android compatibility - #include - #define utmpx utmp - #define setutxent setutent - #define getutxent getutent -#endif +#include "detection/users/users.h" #define FF_USERS_MODULE_NAME "Users" #define FF_USERS_NUM_FORMAT_ARGS 1 void ffPrintUsers(FFinstance* instance) { - struct utmpx* n = NULL; - setutxent(); - FFlist users; - ffListInit(&users, sizeof(n->ut_user) + 1); + ffListInit(&users, sizeof(FFstrbuf)); -next: - while((n = getutxent())) - { - if(n->ut_type == USER_PROCESS) - { - for(uint32_t i = 0; i < users.length; ++i) - { - if(strcmp((const char*)ffListGet(&users, i), n->ut_user) == 0) - goto next; - } - - char* dest = ffListAdd(&users); - strncpy(dest, n->ut_user, sizeof(n->ut_user)); - dest[sizeof(n->ut_user)] = '\0'; - } - } - - if(users.length == 0) + FFstrbuf error; + ffStrbufInit(&error); + + ffDetectUsers(&users, &error); + + if(error.length > 0) { + ffPrintError(instance, FF_USERS_MODULE_NAME, 0, &instance->config.users, "%*s", error.length, error.chars); ffListDestroy(&users); - ffPrintError(instance, FF_USERS_MODULE_NAME, 0, &instance->config.users, "Unable to detect users"); + ffStrbufDestroy(&error); return; } @@ -52,8 +29,11 @@ next: { if(i > 0) ffStrbufAppendS(&result, ", "); - ffStrbufAppendS(&result, (const char*)ffListGet(&users, i)); + FFstrbuf* user = (FFstrbuf*)ffListGet(&users, i); + ffStrbufAppend(&result, user); + ffStrbufDestroy(user); } + ffListDestroy(&users); if(instance->config.users.outputFormat.length == 0) diff --git a/src/modules/weather.c b/src/modules/weather.c index 43c1d56fd..a6bc63ce9 100644 --- a/src/modules/weather.c +++ b/src/modules/weather.c @@ -5,23 +5,24 @@ #define FF_WEATHER_MODULE_NAME "Weather" #define FF_WEATHER_NUM_FORMAT_ARGS 1 -static int sockfd; +static FFNetworkingState state; +static int status = -1; void ffPrepareWeather(FFinstance* instance) { FFstrbuf path; ffStrbufInitS(&path, "/?format="); ffStrbufAppend(&path, &instance->config.weatherOutputFormat); - sockfd = ffNetworkingSendHttpRequest("wttr.in", path.chars, "User-Agent: curl/0.0.0\r\n", instance->config.weatherTimeout); + status = ffNetworkingSendHttpRequest(&state, "wttr.in", path.chars, "User-Agent: curl/0.0.0\r\n"); ffStrbufDestroy(&path); } void ffPrintWeather(FFinstance* instance) { - if(sockfd == 0) + if(status == -1) ffPrepareWeather(instance); - if(sockfd < 0) + if(status == 0) { ffPrintError(instance, FF_WEATHER_MODULE_NAME, 0, &instance->config.weather, "Failed to connect to 'wttr.in'"); return; @@ -29,7 +30,7 @@ void ffPrintWeather(FFinstance* instance) FFstrbuf result; ffStrbufInitA(&result, 4096); - bool success = ffNetworkingRecvHttpResponse(sockfd, &result); + bool success = ffNetworkingRecvHttpResponse(&state, &result, instance->config.weatherTimeout); if (success) ffStrbufSubstrAfterFirstS(&result, "\r\n\r\n"); if(!success || result.length == 0) diff --git a/src/modules/wifi.c b/src/modules/wifi.c new file mode 100644 index 000000000..596c18baa --- /dev/null +++ b/src/modules/wifi.c @@ -0,0 +1,69 @@ +#include "fastfetch.h" +#include "common/printing.h" +#include "detection/wifi/wifi.h" + +#define FF_WIFI_MODULE_NAME "Wifi" +#define FF_WIFI_NUM_FORMAT_ARGS 12 + +void ffPrintWifi(FFinstance* instance) +{ + FFlist result; + ffListInit(&result, sizeof(FFWifiResult)); + + const char* error = ffDetectWifi(instance, &result); + + if(!error) + { + for(uint32_t index = 0; index < result.length; ++index) + { + FFWifiResult* item = (FFWifiResult*)ffListGet(&result, index); + uint8_t moduleIndex = result.length == 1 ? 0 : (uint8_t)(index + 1); + + if(instance->config.wifi.outputFormat.length == 0) + { + ffPrintLogoAndKey(instance, FF_WIFI_MODULE_NAME, moduleIndex, &instance->config.wifi.key); + if(item->conn.ssid.length) + { + printf("%s - %s", item->conn.ssid.chars, item->conn.phyType.chars); + if(!item->security.enabled) + puts(" - insecure"); + else + putchar('\n'); + } + else + { + puts(item->inf.status.chars); + } + } + else + { + ffPrintFormat(instance, FF_WIFI_MODULE_NAME, moduleIndex, &instance->config.wifi, FF_WIFI_NUM_FORMAT_ARGS, (FFformatarg[]){ + {FF_FORMAT_ARG_TYPE_STRBUF, &item->inf.description}, + {FF_FORMAT_ARG_TYPE_STRBUF, &item->inf.status}, + {FF_FORMAT_ARG_TYPE_STRBUF, &item->conn.status}, + {FF_FORMAT_ARG_TYPE_STRBUF, &item->conn.ssid}, + {FF_FORMAT_ARG_TYPE_STRBUF, &item->conn.macAddress}, + {FF_FORMAT_ARG_TYPE_STRBUF, &item->conn.phyType}, + {FF_FORMAT_ARG_TYPE_DOUBLE, &item->conn.signalQuality}, + {FF_FORMAT_ARG_TYPE_DOUBLE, &item->conn.rxRate}, + {FF_FORMAT_ARG_TYPE_DOUBLE, &item->conn.txRate}, + {FF_FORMAT_ARG_TYPE_BOOL, &item->security.enabled}, + {FF_FORMAT_ARG_TYPE_BOOL, &item->security.oneXEnabled}, + {FF_FORMAT_ARG_TYPE_STRBUF, &item->security.algorithm}, + }); + } + + ffStrbufDestroy(&item->inf.description); + ffStrbufDestroy(&item->inf.status); + ffStrbufDestroy(&item->conn.status); + ffStrbufDestroy(&item->conn.ssid); + ffStrbufDestroy(&item->conn.macAddress); + ffStrbufDestroy(&item->conn.phyType); + ffStrbufDestroy(&item->security.algorithm); + } + } + else + { + ffPrintError(instance, FF_WIFI_MODULE_NAME, 0, &instance->config.wmTheme, "%s", error); + } +} diff --git a/src/util/FFlist.h b/src/util/FFlist.h index d1eee05ad..fd7a0f5ca 100644 --- a/src/util/FFlist.h +++ b/src/util/FFlist.h @@ -8,6 +8,7 @@ #include #include #include +#include #define FF_LIST_DEFAULT_ALLOC 16 @@ -39,4 +40,13 @@ static inline void* ffListGet(const FFlist* list, uint32_t index) return list->data + (index * list->elementSize); } +static inline void ffListSort(FFlist* list, int(*compar)(const void*, const void*)) +{ + qsort(list->data, list->length, list->elementSize, compar); +} + +#if defined(_WIN32) || defined(__APPLE__) + #define FF_LIST_AUTO_DESTROY FFlist __attribute__((__cleanup__(ffListDestroy))) +#endif + #endif diff --git a/src/util/FFstrbuf.c b/src/util/FFstrbuf.c index b302bce0a..27150ef24 100644 --- a/src/util/FFstrbuf.c +++ b/src/util/FFstrbuf.c @@ -26,14 +26,23 @@ void ffStrbufInitF(FFstrbuf* strbuf, const char* format, ...) { assert(format != NULL); - ffStrbufInit(strbuf); - va_list arguments; va_start(arguments, format); - ffStrbufAppendVF(strbuf, format, arguments); + ffStrbufInitVF(strbuf, format, arguments); va_end(arguments); } +void ffStrbufInitVF(FFstrbuf* strbuf, const char* format, va_list arguments) +{ + assert(format != NULL); + + int len = vasprintf(&strbuf->chars, format, arguments); + assert(len >= 0); + + strbuf->allocated = (uint32_t)(len + 1); + strbuf->length = (uint32_t)len; +} + uint32_t ffStrbufGetFree(const FFstrbuf* strbuf) { assert(strbuf != NULL); @@ -138,31 +147,43 @@ void ffStrbufAppendVF(FFstrbuf* strbuf, const char* format, va_list arguments) va_copy(copy, arguments); uint32_t free = ffStrbufGetFree(strbuf); - uint32_t written = (uint32_t) vsnprintf(strbuf->chars + strbuf->length, free, format, arguments); + int written = vsnprintf(strbuf->chars + strbuf->length, strbuf->allocated > 0 ? free + 1 : 0, format, arguments); - if(strbuf->length + written > free) + if(written > 0 && strbuf->length + (uint32_t) written > free) { - ffStrbufEnsureFree(strbuf, written); - written = (uint32_t) vsnprintf(strbuf->chars + strbuf->length, ffStrbufGetFree(strbuf), format, copy); + ffStrbufEnsureFree(strbuf, (uint32_t) written); + written = vsnprintf(strbuf->chars + strbuf->length, (uint32_t) written + 1, format, copy); } va_end(copy); - if(written == 0) + if(written > 0) + strbuf->length += (uint32_t) written; +} + +void ffStrbufAppendSUntilC(FFstrbuf* strbuf, const char* value, char until) +{ + if(value == NULL) return; - strbuf->length += written; - strbuf->chars[strbuf->length] = '\0'; + char* end = strchr(value, until); + if(end == NULL) + ffStrbufAppendS(strbuf, value); + else + ffStrbufAppendNS(strbuf, (uint32_t) (end - value), value); } void ffStrbufSetF(FFstrbuf* strbuf, const char* format, ...) { assert(format != NULL); - ffStrbufClear(strbuf); - va_list arguments; va_start(arguments, format); + + if(strbuf->allocated == 0) + return ffStrbufInitVF(strbuf, format, arguments); + + ffStrbufClear(strbuf); ffStrbufAppendVF(strbuf, format, arguments); va_end(arguments); } @@ -426,5 +447,5 @@ void ffStrbufDestroy(FFstrbuf* strbuf) //Avoid free-after-use. These 3 assignments are cheap so don't remove them strbuf->allocated = strbuf->length = 0; free(strbuf->chars); - strbuf->chars = NULL; + strbuf->chars = CHAR_NULL_PTR; } diff --git a/src/util/FFstrbuf.h b/src/util/FFstrbuf.h index 687969606..8116311da 100644 --- a/src/util/FFstrbuf.h +++ b/src/util/FFstrbuf.h @@ -13,6 +13,11 @@ #include #include +#ifdef _WIN32 + #include + #define strcasestr StrStrIA +#endif + #define FASTFETCH_STRBUF_DEFAULT_ALLOC 32 typedef struct FFstrbuf @@ -27,6 +32,7 @@ typedef struct FFstrbuf void ffStrbufInitA(FFstrbuf* strbuf, uint32_t allocate); void ffStrbufInitCopy(FFstrbuf* strbuf, const FFstrbuf* src); void ffStrbufInitF(FFstrbuf* strbuf, const char* format, ...); +void ffStrbufInitVF(FFstrbuf* strbuf, const char* format, va_list arguments); void ffStrbufEnsureFree(FFstrbuf* strbuf, uint32_t free); @@ -40,6 +46,7 @@ void ffStrbufAppendNSExludingC(FFstrbuf* strbuf, uint32_t length, const char* va void ffStrbufAppendTransformS(FFstrbuf* strbuf, const char* value, int(*transformFunc)(int)); FF_C_PRINTF(2, 3) void ffStrbufAppendF(FFstrbuf* strbuf, const char* format, ...); void ffStrbufAppendVF(FFstrbuf* strbuf, const char* format, va_list arguments); +void ffStrbufAppendSUntilC(FFstrbuf* strbuf, const char* value, char until); void ffStrbufPrependNS(FFstrbuf* strbuf, uint32_t length, const char* value); @@ -142,21 +149,46 @@ static inline int ffStrbufComp(const FFstrbuf* strbuf, const FFstrbuf* comp) return memcmp(strbuf->chars, comp->chars, length + 1); } +static inline int ffStrbufCompAlphabetically(const FFstrbuf* strbuf, const FFstrbuf* comp) +{ + return strcmp(strbuf->chars, comp->chars); +} + +static inline FF_C_NODISCARD bool ffStrbufEqual(const FFstrbuf* strbuf, const FFstrbuf* comp) +{ + return ffStrbufComp(strbuf, comp) == 0; +} + static inline FF_C_NODISCARD int ffStrbufCompS(const FFstrbuf* strbuf, const char* comp) { return strcmp(strbuf->chars, comp); } +static inline FF_C_NODISCARD bool ffStrbufEqualS(const FFstrbuf* strbuf, const char* comp) +{ + return ffStrbufCompS(strbuf, comp) == 0; +} + static inline FF_C_NODISCARD int ffStrbufIgnCaseCompS(const FFstrbuf* strbuf, const char* comp) { return strcasecmp(strbuf->chars, comp); } +static inline FF_C_NODISCARD bool ffStrbufIgnCaseEqualS(const FFstrbuf* strbuf, const char* comp) +{ + return ffStrbufIgnCaseCompS(strbuf, comp) == 0; +} + static inline FF_C_NODISCARD int ffStrbufIgnCaseComp(const FFstrbuf* strbuf, const FFstrbuf* comp) { return ffStrbufIgnCaseCompS(strbuf, comp->chars); } +static inline FF_C_NODISCARD bool ffStrbufIgnCaseEqual(const FFstrbuf* strbuf, const FFstrbuf* comp) +{ + return ffStrbufIgnCaseComp(strbuf, comp) == 0; +} + static inline FF_C_NODISCARD bool ffStrbufContainS(const FFstrbuf* strbuf, const char* str) { return strstr(strbuf->chars, str) != NULL; @@ -270,4 +302,9 @@ static inline FF_C_NODISCARD bool ffStrbufEndsWithIgnCase(const FFstrbuf* strbuf { return ffStrbufEndsWithIgnCaseNS(strbuf, end->length, end->chars); } + +#if defined(_WIN32) || defined(__APPLE__) + #define FF_STRBUF_AUTO_DESTROY FFstrbuf __attribute__((__cleanup__(ffStrbufDestroy))) +#endif + #endif diff --git a/src/util/apple/osascript.m b/src/util/apple/osascript.m index 363a50d12..8b01ba069 100644 --- a/src/util/apple/osascript.m +++ b/src/util/apple/osascript.m @@ -4,14 +4,14 @@ #import #import -bool ffOsascript(const char* input, FFstrbuf* result) { - NSString* appleScript = [NSString stringWithUTF8String: input]; - NSAppleScript* script = [[NSAppleScript alloc] initWithSource:appleScript]; +bool ffOsascript(const char* input, FFstrbuf* result) +{ + NSAppleScript* script = [NSAppleScript.alloc initWithSource:@(input)]; NSDictionary* errInfo = nil; NSAppleEventDescriptor* descriptor = [script executeAndReturnError:&errInfo]; if (errInfo) return false; - ffStrbufSetS(result, [[descriptor stringValue] cStringUsingEncoding:NSUTF8StringEncoding]); + ffStrbufSetS(result, descriptor.stringValue.UTF8String); return true; } diff --git a/src/util/mallocHelper.h b/src/util/mallocHelper.h new file mode 100644 index 000000000..a4fd8eb6e --- /dev/null +++ b/src/util/mallocHelper.h @@ -0,0 +1,9 @@ +#include + +static inline void ffWrapFree(void* pPtr) +{ + if(*(void**)pPtr) + free(*(void**)pPtr); +} + +#define FF_AUTO_FREE __attribute__((__cleanup__(ffWrapFree))) diff --git a/src/util/textModifier.h b/src/util/textModifier.h new file mode 100644 index 000000000..f282d31d5 --- /dev/null +++ b/src/util/textModifier.h @@ -0,0 +1,10 @@ +#pragma once + +#ifndef FASTFETCH_INCLUDED_TEXT_MODIFIER +#define FASTFETCH_INCLUDED_TEXT_MODIFIER + +#define FASTFETCH_TEXT_MODIFIER_BOLT "\033[1m" +#define FASTFETCH_TEXT_MODIFIER_ERROR "\033[1;31m" +#define FASTFETCH_TEXT_MODIFIER_RESET "\033[0m" + +#endif diff --git a/src/util/windows/getline.c b/src/util/windows/getline.c new file mode 100644 index 000000000..cb1760f78 --- /dev/null +++ b/src/util/windows/getline.c @@ -0,0 +1,58 @@ +#include "getline.h" + +#include +#include + +ssize_t getline(char **lineptr, size_t *n, FILE *stream) { + ssize_t pos = -1; + int c; + + if (lineptr == NULL || stream == NULL || n == NULL) { + errno = EINVAL; + return -1; + } + + _lock_file(stream); + + c = _getc_nolock(stream); + if (c == EOF) { + goto exit; + } + + if (*lineptr == NULL) { + *lineptr = malloc(128); + if (*lineptr == NULL) { + goto exit; + } + *n = 128; + } + + pos = 0; + while(c != EOF) { + if ((size_t)(pos + 1) >= *n) { + size_t new_size = *n + (*n >> 2); + if (new_size < 128) { + new_size = 128; + } + char *new_ptr = realloc(*lineptr, new_size); + if (new_ptr == NULL) { + pos = -1; + goto exit; + } + *n = new_size; + *lineptr = new_ptr; + } + + ((char *)(*lineptr))[pos ++] = (char)c; + if (c == '\n') { + break; + } + c = _getc_nolock(stream); + } + + (*lineptr)[pos] = '\0'; + +exit: + _unlock_file(stream); + return pos; +} diff --git a/src/util/windows/getline.h b/src/util/windows/getline.h new file mode 100644 index 000000000..e4f48a4cf --- /dev/null +++ b/src/util/windows/getline.h @@ -0,0 +1,11 @@ +#pragma once + +#ifndef FASTFETCH_INCLUDED_UTIL_GETLINE +#define FASTFETCH_INCLUDED_UTIL_GETLINE + +#include +#include + +ssize_t getline(char **lineptr, size_t *n, FILE *stream); + +#endif diff --git a/src/util/windows/manifest.xml b/src/util/windows/manifest.xml new file mode 100644 index 000000000..420545ded --- /dev/null +++ b/src/util/windows/manifest.xml @@ -0,0 +1,23 @@ + + + + + + + + + + + + + + + + + + UTF-8 + true + SegmentHeap + + + diff --git a/src/util/windows/pwd.c b/src/util/windows/pwd.c new file mode 100644 index 000000000..a52f2689a --- /dev/null +++ b/src/util/windows/pwd.c @@ -0,0 +1,17 @@ +#include "pwd.h" + +#define WIN32_LEAN_AND_MEAN +#include +#include + +struct passwd* ffGetPasswd() +{ + static struct passwd res; + + DWORD len = sizeof(res.pw_name); + GetUserNameA(res.pw_name, &len); + + SHGetFolderPathA(NULL, CSIDL_PROFILE, NULL, 0, res.pw_dir); + + return &res; +} diff --git a/src/util/windows/pwd.h b/src/util/windows/pwd.h new file mode 100644 index 000000000..29d4c883b --- /dev/null +++ b/src/util/windows/pwd.h @@ -0,0 +1,22 @@ +#pragma once + +#ifndef FASTFETCH_INCLUDED_UTIL_PWD +#define FASTFETCH_INCLUDED_UTIL_PWD + +#include +#include + +struct passwd +{ + char pw_name[UNLEN + 1]; /* username */ + // char *pw_passwd[1]; /* user password */ + // int pw_uid; /* user ID */ + // int pw_gid; /* group ID */ + // char *pw_gecos; /* user information */ + char pw_dir[MAX_PATH]; /* home directory */ + // char *pw_shell; /* shell program */ +}; + +struct passwd* ffGetPasswd(); + +#endif diff --git a/src/util/windows/registry.c b/src/util/windows/registry.c new file mode 100644 index 000000000..2257e1963 --- /dev/null +++ b/src/util/windows/registry.c @@ -0,0 +1,82 @@ +#include "registry.h" +#include "unicode.h" +#include "util/mallocHelper.h" + +static const char* hKey2Str(HKEY hKey) +{ + #define HKEY_CASE(compareKey) if(hKey == compareKey) return #compareKey; + HKEY_CASE(HKEY_CLASSES_ROOT) + HKEY_CASE(HKEY_CURRENT_USER) + HKEY_CASE(HKEY_LOCAL_MACHINE) + HKEY_CASE(HKEY_USERS) + HKEY_CASE(HKEY_PERFORMANCE_DATA) + HKEY_CASE(HKEY_PERFORMANCE_TEXT) + HKEY_CASE(HKEY_PERFORMANCE_NLSTEXT) + HKEY_CASE(HKEY_CURRENT_CONFIG) + HKEY_CASE(HKEY_DYN_DATA) + HKEY_CASE(HKEY_CURRENT_USER_LOCAL_SETTINGS) + #undef HKEY_CASE + + return "UNKNOWN"; +} + +bool ffRegOpenKeyForRead(HKEY hKey, const wchar_t* subKeyW, HKEY* result, FFstrbuf* error) +{ + if(RegOpenKeyExW(hKey, subKeyW, 0, KEY_READ, result) != ERROR_SUCCESS) + { + if(error) + { + FF_STRBUF_AUTO_DESTROY subKeyA = ffStrbufCreateWS(subKeyW); + ffStrbufAppendF(error, "RegOpenKeyExW(%s\\%s) failed", hKey2Str(hKey), subKeyA.chars); + } + return false; + } + return true; +} + +bool ffRegReadStrbuf(HKEY hKey, const wchar_t* valueNameW, FFstrbuf* result, FFstrbuf* error) +{ + DWORD bufSize; //with tailing '\0' + if(RegGetValueW(hKey, NULL, valueNameW, RRF_RT_REG_SZ, NULL, NULL, &bufSize) != ERROR_SUCCESS) + { + if(error) + { + if(!valueNameW) + valueNameW = L"(default)"; + FF_STRBUF_AUTO_DESTROY valueNameA = ffStrbufCreateWS(valueNameW); + ffStrbufAppendF(error, "RegGetValueA(%s, NULL, RRF_RT_REG_SZ) failed", valueNameA.chars); + } + return false; + } + wchar_t* FF_AUTO_FREE resultW = (wchar_t*)malloc(bufSize); + if(RegGetValueW(hKey, NULL, valueNameW, RRF_RT_REG_SZ, NULL, resultW, &bufSize) != ERROR_SUCCESS) + { + if(error) + { + if(!valueNameW) + valueNameW = L"(default)"; + FF_STRBUF_AUTO_DESTROY valueNameA = ffStrbufCreateWS(valueNameW); + ffStrbufAppendF(error, "RegGetValueA(%s, result, RRF_RT_REG_SZ) failed", valueNameA.chars); + } + return false; + } + ffStrbufSetWS(result, resultW); + return true; +} + +bool ffRegReadUint(HKEY hKey, const wchar_t* valueNameW, uint32_t* result, FFstrbuf* error) +{ + DWORD bufSize = sizeof(*result); + if(RegGetValueW(hKey, NULL, valueNameW, RRF_RT_DWORD, NULL, result, &bufSize) != ERROR_SUCCESS) + { + if(error) + { + if(!valueNameW) + valueNameW = L"(default)"; + FF_STRBUF_AUTO_DESTROY valueNameA = ffStrbufCreateWS(valueNameW); + ffStrbufAppendF(error, "RegGetValueA(%s, result, RRF_RT_DWORD) failed", valueNameA.chars); + } + return false; + } + return true; +} diff --git a/src/util/windows/registry.h b/src/util/windows/registry.h new file mode 100644 index 000000000..22aa6f9ce --- /dev/null +++ b/src/util/windows/registry.h @@ -0,0 +1,23 @@ +#pragma once + +#ifndef FASTFETCH_INCLUDED_REGISTER_H +#define FASTFETCH_INCLUDED_REGISTER_H + +#include "fastfetch.h" + +#define WIN32_LEAN_AND_MEAN +#include + +static inline void wrapRegCloseKey(HKEY* phKey) +{ + if(*phKey) + RegCloseKey(*phKey); +} + +#define FF_HKEY_AUTO_DESTROY HKEY __attribute__((__cleanup__(wrapRegCloseKey))) + +bool ffRegOpenKeyForRead(HKEY hKey, const wchar_t* subKeyW, HKEY* result, FFstrbuf* error); +bool ffRegReadStrbuf(HKEY hKey, const wchar_t* valueNameW, FFstrbuf* result, FFstrbuf* error); +bool ffRegReadUint(HKEY hKey, const wchar_t* valueNameW, uint32_t* result, FFstrbuf* error); + +#endif diff --git a/src/util/windows/unicode.c b/src/util/windows/unicode.c new file mode 100644 index 000000000..b25cdcdd9 --- /dev/null +++ b/src/util/windows/unicode.c @@ -0,0 +1,34 @@ +#include "unicode.h" + +void ffStrbufSetNWS(FFstrbuf* result, uint32_t length, const wchar_t* source) +{ + if(!length) + { + ffStrbufClear(result); + return; + } + + int size_needed = WideCharToMultiByte(CP_UTF8, 0, source, (int)length, NULL, 0, NULL, NULL); + ffStrbufEnsureFree(result, (uint32_t)size_needed); + WideCharToMultiByte(CP_UTF8, 0, source, (int)length, result->chars, size_needed, NULL, NULL); + result->length = (uint32_t)size_needed; + result->chars[size_needed] = '\0'; +} + +FFstrbuf ffStrbufCreateNWS(uint32_t length, const wchar_t* source) +{ + FFstrbuf result; + + if(length == 0) + ffStrbufInit(&result); + else + { + int size_needed = WideCharToMultiByte(CP_UTF8, 0, source, (int)length, NULL, 0, NULL, NULL); + ffStrbufInitA(&result, (uint32_t)size_needed); + WideCharToMultiByte(CP_UTF8, 0, source, (int)length, result.chars, size_needed, NULL, NULL); + result.length = (uint32_t)size_needed; + result.chars[size_needed] = '\0'; + } + + return result; +} diff --git a/src/util/windows/unicode.h b/src/util/windows/unicode.h new file mode 100644 index 000000000..d70fbb22e --- /dev/null +++ b/src/util/windows/unicode.h @@ -0,0 +1,21 @@ +#pragma once + +#ifndef FASTFETCH_INCLUDED_UNICODE_H +#define FASTFETCH_INCLUDED_UNICODE_H + +#include "fastfetch.h" +#include + +void ffStrbufSetNWS(FFstrbuf* result, uint32_t length, const wchar_t* source); +static inline void ffStrbufSetWS(FFstrbuf* result, const wchar_t* source) +{ + return ffStrbufSetNWS(result, (uint32_t)wcslen(source), source); +} + +FFstrbuf ffStrbufCreateNWS(uint32_t length, const wchar_t* source); +static inline FFstrbuf ffStrbufCreateWS(const wchar_t* source) +{ + return ffStrbufCreateNWS((uint32_t)wcslen(source), source); +} + +#endif diff --git a/src/util/windows/utsname.c b/src/util/windows/utsname.c new file mode 100644 index 000000000..a1f68021d --- /dev/null +++ b/src/util/windows/utsname.c @@ -0,0 +1,116 @@ +#include "fastfetch.h" +#include "utsname.h" + +#define WIN32_LEAN_AND_MEAN 1 +#include + +static int detectSysname(struct utsname *name) +{ + strncpy(name->sysname, "Windows_NT", UTSNAME_MAXLENGTH); + return 0; +} + +static int detectNodename(struct utsname *name) +{ + DWORD bufSize = UTSNAME_MAXLENGTH - 1; + if(!GetComputerNameA(name->nodename, &bufSize)) + return 1; + name->nodename[bufSize] = '\0'; + return 0; +} + +static int detectVersion(struct utsname *name) +{ + HKEY hKey; + if(RegOpenKeyExW(HKEY_LOCAL_MACHINE, L"SOFTWARE\\Microsoft\\Windows NT\\CurrentVersion", 0, KEY_QUERY_VALUE, &hKey) != ERROR_SUCCESS) + return 1; + + DWORD bufSize; + + char currentVersion[32]; + + { + DWORD currentMajorVersionNumber; + DWORD currentMinorVersionNumber; + bufSize = sizeof(currentMajorVersionNumber); + if(RegGetValueW(hKey, NULL, L"CurrentMajorVersionNumber", RRF_RT_REG_DWORD, NULL, ¤tMajorVersionNumber, &bufSize) == ERROR_SUCCESS && + RegGetValueW(hKey, NULL, L"CurrentMinorVersionNumber", RRF_RT_REG_DWORD, NULL, ¤tMinorVersionNumber, &bufSize) == ERROR_SUCCESS + ) + snprintf(currentVersion, sizeof(currentVersion), "%u.%u", (unsigned)currentMajorVersionNumber, (unsigned)currentMinorVersionNumber); + else + { + bufSize = sizeof(currentVersion); + if(RegGetValueA(hKey, NULL, "CurrentVersion", RRF_RT_REG_SZ, NULL, currentVersion, &bufSize) != ERROR_SUCCESS) + strcpy(currentVersion, "0.0"); + } + } + + char currentBuildNumber[32]; + bufSize = sizeof(currentBuildNumber); + if(RegGetValueA(hKey, NULL, "CurrentBuildNumber", RRF_RT_REG_SZ, NULL, currentBuildNumber, &bufSize) != ERROR_SUCCESS) + strcpy(currentBuildNumber, "0"); + + DWORD ubr; + bufSize = sizeof(ubr); + if(RegGetValueA(hKey, NULL, "UBR", RRF_RT_REG_DWORD, NULL, &ubr, &bufSize) != ERROR_SUCCESS || bufSize != sizeof(ubr)) + ubr = 0; + + snprintf(name->release, sizeof(name->release), "%s.%s.%u", currentVersion, currentBuildNumber, (unsigned)ubr); + + bufSize = sizeof(name->version); + RegGetValueA(hKey, NULL, "DisplayVersion", RRF_RT_REG_SZ, NULL, name->version, &bufSize); + + RegCloseKey(hKey); + return 0; +} + +static int detectMachine(struct utsname *name) +{ + // Get hardware info + SYSTEM_INFO sysInfo = {0}; + GetNativeSystemInfo(&sysInfo); + + // Set processor architecture + switch(sysInfo.wProcessorArchitecture) + { + case PROCESSOR_ARCHITECTURE_AMD64: + strcpy(name->machine, "x86_64"); + break; + case PROCESSOR_ARCHITECTURE_IA64: + strcpy(name->machine, "ia64"); + break; + case PROCESSOR_ARCHITECTURE_INTEL: + strcpy(name->machine, "x86"); + break; + case PROCESSOR_ARCHITECTURE_ARM64: + strcpy(name->machine, "aarch64"); + break; + case PROCESSOR_ARCHITECTURE_ARM: + strcpy(name->machine, "arm"); + break; + case PROCESSOR_ARCHITECTURE_PPC: + strcpy(name->machine, "ppc"); + break; + case PROCESSOR_ARCHITECTURE_MIPS: + strcpy(name->machine, "mips"); + break; + case PROCESSOR_ARCHITECTURE_UNKNOWN: + default: + strcpy(name->machine, "unknown"); + break; + } + + return 0; +} + +int uname(struct utsname *name) +{ + memset(name, 0, sizeof(*name)); + + int sysnameResult = detectSysname(name); + int nodenameResult = detectNodename(name); + int versionResult = detectVersion(name); + int machineResult = detectMachine(name); + + return sysnameResult || nodenameResult || versionResult || machineResult; +} diff --git a/src/util/windows/utsname.h b/src/util/windows/utsname.h new file mode 100644 index 000000000..96bc53adc --- /dev/null +++ b/src/util/windows/utsname.h @@ -0,0 +1,19 @@ +#pragma once + +#ifndef FASTFETCH_INCLUDED_UTSNAME_H +#define FASTFETCH_INCLUDED_UTSNAME_H + +#define UTSNAME_MAXLENGTH 256 + +struct utsname +{ + char sysname[UTSNAME_MAXLENGTH]; // name of this implementation of the operating system + char nodename[UTSNAME_MAXLENGTH]; // name of this node within an implementation - dependent communications network + char release[UTSNAME_MAXLENGTH]; // current release level of this implementation + char version[UTSNAME_MAXLENGTH]; // current version level of this release + char machine[UTSNAME_MAXLENGTH]; // name of the hardware type on which the system is running +}; + +int uname(struct utsname *name); + +#endif diff --git a/src/util/windows/version.rc b/src/util/windows/version.rc new file mode 100644 index 000000000..e130a65e9 --- /dev/null +++ b/src/util/windows/version.rc @@ -0,0 +1,54 @@ +// +// Include the necessary resources +// +#include +#include +#include +#include "fastfetch_config.h" + +#ifdef RC_INVOKED + +// +// Set up debug information +// +#if DEBUG +#define VER_DEBUG VS_FF_DEBUG +#else +#define VER_DEBUG 0 +#endif + +#define FF_TO_STR1(str) #str +#define FF_TO_STR(str) FF_TO_STR1(str) + +CREATEPROCESS_MANIFEST_RESOURCE_ID RT_MANIFEST "manifest.xml" + +// ------- version info ------------------------------------------------------- + +VS_VERSION_INFO VERSIONINFO +FILEVERSION FASTFETCH_PROJECT_VERSION_MAJOR,FASTFETCH_PROJECT_VERSION_MINOR,FASTFETCH_PROJECT_VERSION_PATCH,FASTFETCH_PROJECT_VERSION_TWEAK_NUM +PRODUCTVERSION FASTFETCH_PROJECT_VERSION_MAJOR,FASTFETCH_PROJECT_VERSION_MINOR,FASTFETCH_PROJECT_VERSION_PATCH,FASTFETCH_PROJECT_VERSION_TWEAK_NUM +FILEFLAGSMASK VS_FFI_FILEFLAGSMASK +FILEFLAGS (VER_DEBUG|VS_FF_PRERELEASE) +FILEOS VOS_NT +FILETYPE VFT_APP +BEGIN + BLOCK "StringFileInfo" + BEGIN + BLOCK "040904b0" + BEGIN + VALUE "Comments", FASTFETCH_PROJECT_HOMEPAGE_URL + VALUE "FileDescription", FF_TO_STR(FASTFETCH_TARGET_BINARY_NAME) " - " FASTFETCH_PROJECT_DESCRIPTION + VALUE "FileVersion", FASTFETCH_PROJECT_VERSION FASTFETCH_PROJECT_VERSION_TWEAK + VALUE "InternalName", FF_TO_STR(FASTFETCH_TARGET_BINARY_NAME) ".exe" + VALUE "LegalCopyright", FASTFETCH_PROJECT_LICENSE + VALUE "OriginalFilename", FF_TO_STR(FASTFETCH_TARGET_BINARY_NAME) ".exe" + VALUE "ProductName", FASTFETCH_PROJECT_NAME + VALUE "ProductVersion", FASTFETCH_PROJECT_VERSION FASTFETCH_PROJECT_VERSION_TWEAK + END + END + BLOCK "VarFileInfo" + BEGIN + VALUE "Translation", 0x0409,1252 + END +END +#endif diff --git a/src/util/windows/wmi.cpp b/src/util/windows/wmi.cpp new file mode 100644 index 000000000..cba0c07c5 --- /dev/null +++ b/src/util/windows/wmi.cpp @@ -0,0 +1,320 @@ +#include "wmi.hpp" + +#include +#include +#include + +namespace +{ + // Provide our bstr_t to avoid libstdc++ dependency + struct bstr_t + { + explicit bstr_t(const wchar_t* str) noexcept: _bstr(SysAllocString(str)) {} + ~bstr_t() noexcept { SysFreeString(_bstr); } + explicit operator const wchar_t*() const noexcept { return _bstr; } + operator BSTR() const noexcept { return _bstr; } + + private: + BSTR _bstr; + }; +} + +//https://learn.microsoft.com/en-us/windows/win32/wmisdk/example--getting-wmi-data-from-the-local-computer +//https://learn.microsoft.com/en-us/windows/win32/cimwin32prov/computer-system-hardware-classes +static void CoUninitializeWrap() +{ + CoUninitialize(); +} + +static BOOL CALLBACK InitHandleFunction(PINIT_ONCE, PVOID, PVOID *lpContext) +{ + HRESULT hres; + + // Initialize COM + hres = CoInitializeEx(nullptr, COINIT_MULTITHREADED); + + // Set general COM security levels + hres = CoInitializeSecurity( + nullptr, + -1, // COM authentication + nullptr, // Authentication services + nullptr, // Reserved + RPC_C_AUTHN_LEVEL_DEFAULT, // Default authentication + RPC_C_IMP_LEVEL_IMPERSONATE, // Default Impersonation + nullptr, // Authentication info + EOAC_NONE, // Additional capabilities + nullptr // Reserved + ); + + if (FAILED(hres)) + { + CoUninitialize(); + *((const char**)lpContext) = "Failed to initialize security"; + return FALSE; + } + + // Obtain the initial locator to WMI + IWbemLocator* pLoc = nullptr; + hres = CoCreateInstance( + CLSID_WbemLocator, + nullptr, + CLSCTX_INPROC_SERVER, + IID_IWbemLocator, + (LPVOID*) &pLoc); + + if (FAILED(hres)) + { + CoUninitialize(); + *((const char**)lpContext) = "Failed to create IWbemLocator object"; + return FALSE; + } + + // Connect to WMI through the IWbemLocator::ConnectServer method + IWbemServices* pSvc = nullptr; + + // Connect to the root\cimv2 namespace with + // the current user and obtain pointer pSvc + // to make IWbemServices calls. + hres = pLoc->ConnectServer( + bstr_t(L"ROOT\\CIMV2"), // Object path of WMI namespace + nullptr, // User name. nullptr = current user + nullptr, // User password. nullptr = current + 0, // Locale. nullptr indicates current + 0, // Security flags. + 0, // Authority (for example, Kerberos) + 0, // Context object + &pSvc // pointer to IWbemServices proxy + ); + pLoc->Release(); + pLoc = nullptr; + + if (FAILED(hres)) + { + CoUninitialize(); + *((const char**)lpContext) = "Could not connect WMI server"; + return FALSE; + } + + // Set security levels on the proxy ------------------------- + hres = CoSetProxyBlanket( + pSvc, // Indicates the proxy to set + RPC_C_AUTHN_WINNT, // RPC_C_AUTHN_xxx + RPC_C_AUTHZ_NONE, // RPC_C_AUTHZ_xxx + nullptr, // Server principal name + RPC_C_AUTHN_LEVEL_CALL, // RPC_C_AUTHN_LEVEL_xxx + RPC_C_IMP_LEVEL_IMPERSONATE, // RPC_C_IMP_LEVEL_xxx + nullptr, // client identity + EOAC_NONE // proxy capabilities + ); + + if (FAILED(hres)) + { + pSvc->Release(); + CoUninitialize(); + *((const char**)lpContext) = "Could not set proxy blanket"; + return FALSE; + } + + *((IWbemServices**)lpContext) = pSvc; + atexit(CoUninitializeWrap); + return TRUE; +} + +FFWmiQuery::FFWmiQuery(const wchar_t* queryStr, FFstrbuf* error) + : pEnumerator(nullptr) +{ + static INIT_ONCE s_InitOnce = INIT_ONCE_STATIC_INIT; + const char* context; + if (InitOnceExecuteOnce(&s_InitOnce, &InitHandleFunction, nullptr, (void**)&context) == FALSE) + { + if(error) + ffStrbufAppendS(error, context); + return; + } + + // Use the IWbemServices pointer to make requests of WMI + HRESULT hres; + + hres = ((IWbemServices*)context)->ExecQuery( + bstr_t(L"WQL"), + bstr_t(queryStr), + WBEM_FLAG_FORWARD_ONLY | WBEM_FLAG_RETURN_IMMEDIATELY, + nullptr, + &pEnumerator); + + if (FAILED(hres)) + { + if(error) + ffStrbufAppendF(error, "Query for '%ls' failed. Error code = 0x%lX", queryStr, hres); + } +} + +static void ffBstrToStrbuf(BSTR bstr, FFstrbuf* strbuf) +{ + int len = (int)SysStringLen(bstr); + if(len <= 0) + { + ffStrbufClear(strbuf); + return; + } + int size_needed = WideCharToMultiByte(CP_UTF8, 0, bstr, len, nullptr, 0, nullptr, nullptr); + ffStrbufEnsureFree(strbuf, (uint32_t)size_needed); + WideCharToMultiByte(CP_UTF8, 0, bstr, len, strbuf->chars, size_needed, nullptr, nullptr); + strbuf->length = (uint32_t)size_needed; + strbuf->chars[size_needed] = '\0'; +} + +bool FFWmiRecord::getString(const wchar_t* key, FFstrbuf* strbuf) +{ + bool result = true; + + VARIANT vtProp; + VariantInit(&vtProp); + + CIMTYPE type; + if(FAILED(obj->Get(key, 0, &vtProp, &type, nullptr)) || vtProp.vt != VT_BSTR) + { + result = false; + } + else + { + switch(vtProp.vt) + { + case VT_BSTR: + if(type == CIM_DATETIME) + { + ISWbemDateTime *pDateTime; + BSTR dateStr; + if(FAILED(CoCreateInstance(__uuidof(SWbemDateTime), 0, CLSCTX_INPROC_SERVER, IID_PPV_ARGS(&pDateTime)))) + result = false; + else if(FAILED(pDateTime->put_Value(vtProp.bstrVal))) + result = false; + else if(FAILED(pDateTime->GetFileTime(VARIANT_TRUE, &dateStr))) + result = false; + else + ffBstrToStrbuf(dateStr, strbuf); + } + else + { + ffBstrToStrbuf(vtProp.bstrVal, strbuf); + } + break; + + case VT_LPSTR: + ffStrbufAppendS(strbuf, vtProp.pcVal); + break; + + case VT_LPWSTR: // TODO + default: + result = false; + break; + } + } + VariantClear(&vtProp); + return result; +} + +bool FFWmiRecord::getSigned(const wchar_t* key, int64_t* integer) +{ + bool result = true; + + VARIANT vtProp; + VariantInit(&vtProp); + + CIMTYPE type; + if(FAILED(obj->Get(key, 0, &vtProp, &type, nullptr))) + { + result = false; + } + else + { + switch(vtProp.vt) + { + case VT_BSTR: *integer = wcstoll(vtProp.bstrVal, nullptr, 10); break; + case VT_I1: *integer = vtProp.cVal; break; + case VT_I2: *integer = vtProp.iVal; break; + case VT_INT: + case VT_I4: *integer = vtProp.intVal; break; + case VT_I8: *integer = vtProp.llVal; break; + case VT_UI1: *integer = (int64_t)vtProp.bVal; break; + case VT_UI2: *integer = (int64_t)vtProp.uiVal; break; + case VT_UINT: + case VT_UI4: *integer = (int64_t)vtProp.uintVal; break; + case VT_UI8: *integer = (int64_t)vtProp.ullVal; break; + case VT_BOOL: *integer = vtProp.boolVal != VARIANT_FALSE; break; + default: *integer = 0; result = false; + } + } + VariantClear(&vtProp); + return result; +} + +bool FFWmiRecord::getUnsigned(const wchar_t* key, uint64_t* integer) +{ + bool result = true; + + VARIANT vtProp; + VariantInit(&vtProp); + + if(FAILED(obj->Get(key, 0, &vtProp, nullptr, nullptr))) + { + result = false; + } + else + { + switch(vtProp.vt) + { + case VT_BSTR: *integer = wcstoull(vtProp.bstrVal, nullptr, 10); break; + case VT_I1: *integer = (uint64_t)vtProp.cVal; break; + case VT_I2: *integer = (uint64_t)vtProp.iVal; break; + case VT_INT: + case VT_I4: *integer = (uint64_t)vtProp.intVal; break; + case VT_I8: *integer = (uint64_t)vtProp.llVal; break; + case VT_UI1: *integer = vtProp.bVal; break; + case VT_UI2: *integer = vtProp.uiVal; break; + case VT_UINT: + case VT_UI4: *integer = vtProp.uintVal; break; + case VT_UI8: *integer = vtProp.ullVal; break; + case VT_BOOL: *integer = vtProp.boolVal != VARIANT_FALSE; break; + default: *integer = 0; result = false; + } + } + VariantClear(&vtProp); + return result; +} + +bool FFWmiRecord::getReal(const wchar_t* key, double* real) +{ + bool result = true; + + VARIANT vtProp; + VariantInit(&vtProp); + + if(FAILED(obj->Get(key, 0, &vtProp, nullptr, nullptr))) + { + result = false; + } + else + { + switch(vtProp.vt) + { + case VT_BSTR: *real = wcstod(vtProp.bstrVal, nullptr); break; + case VT_I1: *real = vtProp.cVal; break; + case VT_I2: *real = vtProp.iVal; break; + case VT_INT: + case VT_I4: *real = vtProp.intVal; break; + case VT_I8: *real = (double)vtProp.llVal; break; + case VT_UI1: *real = vtProp.bVal; break; + case VT_UI2: *real = vtProp.uiVal; break; + case VT_UINT: + case VT_UI4: *real = vtProp.uintVal; break; + case VT_UI8: *real = (double)vtProp.ullVal; break; + case VT_R4: *real = vtProp.fltVal; break; + case VT_R8: *real = vtProp.dblVal; break; + case VT_BOOL: *real = vtProp.boolVal != VARIANT_FALSE; break; + default: *real = NAN; result = false; + } + } + VariantClear(&vtProp); + return result; +} diff --git a/src/util/windows/wmi.hpp b/src/util/windows/wmi.hpp new file mode 100644 index 000000000..6f7d214cb --- /dev/null +++ b/src/util/windows/wmi.hpp @@ -0,0 +1,71 @@ +#pragma once + +#ifndef FF_INCLUDED_util_windows_wmi +#define FF_INCLUDED_util_windows_wmi + +#ifdef __cplusplus + +extern "C" { + #include "util/FFstrbuf.h" +} + +#include + +struct FFWmiRecord +{ + IWbemClassObject* obj; + + explicit FFWmiRecord(IEnumWbemClassObject* pEnumerator): obj(nullptr) { + if(!pEnumerator) return; + + ULONG ret; + bool ok = SUCCEEDED(pEnumerator->Next((LONG)WBEM_INFINITE, 1, &obj, &ret)) && ret; + if(!ok) obj = nullptr; + } + FFWmiRecord(const FFWmiRecord&) = delete; + FFWmiRecord(FFWmiRecord&& other) { *this = (FFWmiRecord&&)other; } + ~FFWmiRecord() { if(obj) obj->Release(); } + explicit operator bool() { return !!obj; } + FFWmiRecord& operator =(FFWmiRecord&& other) { + if(obj) obj->Release(); + obj = other.obj; + other.obj = nullptr; + return *this; + } + + bool getString(const wchar_t* key, FFstrbuf* strbuf); + bool getSigned(const wchar_t* key, int64_t* integer); + bool getUnsigned(const wchar_t* key, uint64_t* integer); + bool getReal(const wchar_t* key, double* real); +}; + +struct FFWmiQuery +{ + IEnumWbemClassObject* pEnumerator = nullptr; + + FFWmiQuery(const wchar_t* queryStr, FFstrbuf* error = nullptr); + explicit FFWmiQuery(IEnumWbemClassObject* pEnumerator): pEnumerator(pEnumerator) {} + FFWmiQuery(const FFWmiQuery& other) = delete; + FFWmiQuery(FFWmiQuery&& other) { *this = (FFWmiQuery&&)other; } + ~FFWmiQuery() { if(pEnumerator) pEnumerator->Release(); } + + explicit operator bool() { return !!pEnumerator; } + FFWmiQuery& operator =(FFWmiQuery&& other) { + if(pEnumerator) pEnumerator->Release(); + pEnumerator = other.pEnumerator; + other.pEnumerator = nullptr; + return *this; + } + + FFWmiRecord next() { + FFWmiRecord result(pEnumerator); + return result; + } +}; + +#else + // Win32 COM headers requires C++ compiler + #error Must be included in C++ source file +#endif //__cplusplus + +#endif //FF_INCLUDED_util_windows_wmi diff --git a/tests/list.c b/tests/list.c index 38be491e5..e9c10423e 100644 --- a/tests/list.c +++ b/tests/list.c @@ -1,8 +1,10 @@ -#include "fastfetch.h" +#include "util/FFlist.h" +#include "util/textModifier.h" #include #include #include +#include __attribute__((__noreturn__)) static void testFailed(const FFlist* list, const char* expression, int lineNo) @@ -26,10 +28,8 @@ static bool numEqualsAdapter(const void* first, const void* second) #define VERIFY(expression) if(!(expression)) testFailed(&list, #expression, __LINE__) -int main(int argc, char** argv) +int main(void) { - FF_UNUSED(argc, argv) - FFlist list; //initA diff --git a/tests/strbuf.c b/tests/strbuf.c index 403da59d5..dd8b6d57d 100644 --- a/tests/strbuf.c +++ b/tests/strbuf.c @@ -1,7 +1,9 @@ -#include "fastfetch.h" +#include "util/FFstrbuf.h" +#include "util/textModifier.h" #include #include +#include __attribute__((__noreturn__)) static void testFailed(const FFstrbuf* strbuf, const char* expression, int lineNo) @@ -17,14 +19,12 @@ static void testFailed(const FFstrbuf* strbuf, const char* expression, int lineN #define VERIFY(expression) if(!(expression)) testFailed(&strbuf, #expression, __LINE__) int shouldNotBeCalled(int c) { - FF_UNUSED(c); + (void)c; exit(1); } -int main(int argc, char** argv) +int main(void) { - FF_UNUSED(argc, argv) - FFstrbuf strbuf; //destroy 0 @@ -63,7 +63,7 @@ int main(int argc, char** argv) VERIFY(strbuf.length == 5); VERIFY(strbuf.allocated >= 6); - VERIFY(ffStrbufCompS(&strbuf, "12345") == 0); + VERIFY(ffStrbufEqualS(&strbuf, "12345")); //appendNS @@ -72,7 +72,7 @@ int main(int argc, char** argv) VERIFY(strbuf.length == 9); VERIFY(strbuf.allocated >= 10); - VERIFY(ffStrbufCompS(&strbuf, "123456789") == 0); + VERIFY(ffStrbufEqualS(&strbuf, "123456789")); //appendS long @@ -87,7 +87,7 @@ int main(int argc, char** argv) VERIFY(strbuf.length == 9); VERIFY(strbuf.allocated >= 110); VERIFY(strbuf.chars[strbuf.length] == 0); - VERIFY(ffStrbufCompS(&strbuf, "123456789") == 0); + VERIFY(ffStrbufEqualS(&strbuf, "123456789")); //startsWithC @@ -166,10 +166,16 @@ int main(int argc, char** argv) VERIFY(!ffStrbufEndsWithIgnCaseS(&strbuf, "0aBcDeFg")); //ensure - ffStrbufEnsureEndsWithC(&strbuf, '^'); - VERIFY(ffStrbufCompS(&strbuf, "^aBcDeFg")); ffStrbufEnsureEndsWithC(&strbuf, '$'); - VERIFY(ffStrbufCompS(&strbuf, "^aBcDeFg$")); + VERIFY(ffStrbufEqualS(&strbuf, "AbCdEfG$")); + ffStrbufEnsureEndsWithC(&strbuf, '$'); + VERIFY(ffStrbufEqualS(&strbuf, "AbCdEfG$")); + + //clear + ffStrbufClear(&strbuf); + VERIFY(strbuf.allocated > 0); + VERIFY(strbuf.length == 0); + VERIFY(strbuf.chars && strbuf.chars[0] == 0); //Destroy @@ -177,7 +183,28 @@ int main(int argc, char** argv) VERIFY(strbuf.allocated == 0); VERIFY(strbuf.length == 0); - VERIFY(strbuf.chars == NULL); + VERIFY(strbuf.chars && strbuf.chars[0] == 0); + + //initA + ffStrbufInitA(&strbuf, 32); + + VERIFY(strbuf.allocated == 32); + VERIFY(strbuf.length == 0); + VERIFY(strbuf.chars && strbuf.chars[0] == 0); + + //appendF + ffStrbufAppendF(&strbuf, "%s", "1234567890123456789012345678901"); + VERIFY(strbuf.allocated == 32); + VERIFY(ffStrbufEqualS(&strbuf, "1234567890123456789012345678901")); + + ffStrbufDestroy(&strbuf); + + //initF + ffStrbufInitF(&strbuf, "%s", "1234567890123456789012345678901"); + VERIFY(strbuf.allocated == 32); + VERIFY(ffStrbufEqualS(&strbuf, "1234567890123456789012345678901")); + + ffStrbufDestroy(&strbuf); //Success puts("\033[32mAll tests passed!"FASTFETCH_TEXT_MODIFIER_RESET);