diff --git a/.github/workflows/push.yml b/.github/workflows/push.yml index 006e26d22..3fefdb776 100644 --- a/.github/workflows/push.yml +++ b/.github/workflows/push.yml @@ -18,7 +18,7 @@ jobs: uses: actions/checkout@v3 - name: install required packages - run: sudo apt-get update && sudo apt-get install -y libpci-dev libvulkan-dev libwayland-dev libxrandr-dev libxcb-randr0-dev libdconf-dev libdbus-1-dev libmagickcore-dev libxfconf-0-dev libsqlite3-dev rpm librpm-dev libzstd-dev libegl-dev libglx-dev libosmesa6-dev ocl-icd-opencl-dev libcjson-dev + run: sudo apt-get update && sudo apt-get install -y libpci-dev libvulkan-dev libwayland-dev libxrandr-dev libxcb-randr0-dev libdconf-dev libdbus-1-dev libmagickcore-dev libxfconf-0-dev libsqlite3-dev rpm librpm-dev libzstd-dev libegl-dev libglx-dev libosmesa6-dev ocl-icd-opencl-dev libcjson-dev libnm-dev - name: Initialize CodeQL uses: github/codeql-action/init@v2 @@ -34,6 +34,9 @@ jobs: - name: perform CodeQL analysis uses: github/codeql-action/analyze@v2 + - name: list features + run: ./fastfetch --list-features + - name: run fastfetch run: time ./fastfetch --disable-linewrap false --hide-cursor false --show-errors true --load-config presets/all @@ -80,6 +83,9 @@ jobs: - name: perform CodeQL analysis uses: github/codeql-action/analyze@v2 + - name: list features + run: ./fastfetch --list-features + - name: run fastfetch run: time ./fastfetch --disable-linewrap false --hide-cursor false --show-errors true --load-config presets/all @@ -113,6 +119,7 @@ jobs: run: | cmake -DSET_TWEAK=Off -DBUILD_TESTS=On . cmake --build . --target package + ./fastfetch --list-features time ./fastfetch --disable-linewrap false --hide-cursor false --show-errors true --load-config presets/all time ./flashfetch ctest @@ -179,6 +186,9 @@ jobs: - name: copy necessary dlls run: cp /clang64/bin/{libcjson,libOpenCL,vulkan-1}.dll . + - name: list features + run: ./fastfetch --list-features + - name: run fastfetch run: time ./fastfetch --disable-linewrap false --hide-cursor false --show-errors true --load-config presets/all @@ -197,80 +207,6 @@ jobs: name: fastfetch-windows path: ./fastfetch-*-Windows.zip - win7: - name: Win7 - 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 zip 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: create zip archive - run: zip fastfetch-$(./fastfetch --version-raw)-Win7.zip *.dll fastfetch.exe flashfetch.exe - - - name: upload artifacts - uses: actions/upload-artifact@v3 - with: - name: fastfetch-win7 - path: ./fastfetch-*-Win7.zip - release: if: github.event_name == 'push' && github.ref == 'refs/heads/master' && github.repository == 'LinusDierheimer/fastfetch' name: Release @@ -280,7 +216,6 @@ jobs: - macos - freebsd - windows - - win7 permissions: contents: write steps: @@ -300,4 +235,4 @@ jobs: tag: ${{ needs.linux.outputs.ffversion }} commit: ${{ github.sha }} artifactErrorsFailBuild: true - artifacts: fastfetch-linux/*,fastfetch-macos/*,fastfetch-freebsd/*,fastfetch-windows/*,fastfetch-win7/* + artifacts: fastfetch-linux/*,fastfetch-macos/*,fastfetch-freebsd/*,fastfetch-windows/* diff --git a/CHANGELOG.md b/CHANGELOG.md index f4f01fb0d..b4fba05d4 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,3 +1,49 @@ +# 1.9.0 + +Notable Changes: +* fastfetch no longer creates a sample config file silently. Use `--gen-config` to generate one. +* fastfetch now search for user config file in the order of `fastfetch --list-config-paths` +* Unknown disks are hidden by default. +* `Resolution` module is renamed to `Display`. (#393) + +Features: +* `--logo-padding-top` option (@CarterLi, #372) +* Raw image file as logo support (@CarterLi) +* Look for config files in `$APPDATA` ([RoamingAppData](https://superuser.com/questions/21458/why-are-there-directories-called-local-locallow-and-roaming-under-users-user#answer-21462)) (Windows) +* Look for config files in `~/Library/Preferences` (macOS) +* Add `--list-config-paths` option which list search paths of config files +* Add `--list-data-paths` option which list search paths for presets and logos +* Add `Brightness` module support +* Add `Battery` module support for FreeBSD +* Add `--disk-show-unknown` option for Disk module +* Add `--disk-show-subvolumes` option for Disk module +* Add `--gpu-hide-integrated` option (#379) +* Add `--gpu-hide-discrete` option (#379) +* Detect terminal version when available +* Support `WezTerm` terminal font detection (requires [`wezterm` executable](https://wezfurlong.org/wezterm/cli/general.html) being available) +* Add `--shell-version` and `--terminal-version` options to disable shell / terminal version detection +* Enhance `--percent-type` to allow hiding other texts (#387) +* Add Wifi module support for Linux +* Detect scaled resolutions (Windows, macOS) +* Optimise font module printing (Windows) +* Detect pacman package count inside MSYS2 environment (Windows) +* Add Wifi / Battery module support for Android +* Disk name support for Linux + +Logos: +* Raspbian (@IamNoRobot, #373) + +Bugfixes: +* `--logo-type` now does accept `iterm` too (@CarterLi, #374) +* Fix mintty terminal font detection (Windows) +* Fix bug that line buffering doesn't work properly (Windows) +* Fix rpm package count detection (Linux) +* Fix cpu temp detection (Linux) + +Other: +* Fixed a Typo in iterm error message (@jessebot, #376) +* Don't try to load config file in `/etc` (Windows) + # 1.8.2 Bugfixes: @@ -112,7 +158,7 @@ Bugfixes: * Don't segfault in GPU code on Intel Macs (@CarterLi, #236) * Don't use hardcoded size units in presets (@dr460nf1r3, #255) * Don't crash with some format strings (#252) -* --logo-none keeps key color now (#264) +* --logo none keeps key color now (#264) # 1.7.2 diff --git a/CMakeLists.txt b/CMakeLists.txt index 3b777ba3b..0d257dab7 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -1,7 +1,7 @@ cmake_minimum_required(VERSION 3.12.0) # target_link_libraries with OBJECT libs & project homepage url project(fastfetch - VERSION 1.8.2 + VERSION 1.9.0 LANGUAGES C DESCRIPTION "Fast system information tool" HOMEPAGE_URL "https://github.com/LinusDierheimer/fastfetch" @@ -68,6 +68,7 @@ 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 OR WIN32" OFF) cmake_dependent_option(ENABLE_LIBCJSON "Enable libcjson" ON "LINUX OR WIN32" OFF) +cmake_dependent_option(ENABLE_LIBNM "Enable libnm" ON "LINUX" 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) @@ -102,6 +103,7 @@ 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") + set(CMAKE_EXE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS} -Wl,--tsaware -Wl,--build-id -Wl,--subsystem,console:6.1,--major-os-version,6,--minor-os-version,1") endif() # Used for dlopen finding dylibs installed by homebrew @@ -244,7 +246,6 @@ set(LIBFASTFETCH_SRC 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 @@ -254,6 +255,7 @@ set(LIBFASTFETCH_SRC src/modules/battery.c src/modules/bios.c src/modules/board.c + src/modules/brightness.c src/modules/break.c src/modules/chassis.c src/modules/colors.c @@ -261,6 +263,7 @@ set(LIBFASTFETCH_SRC src/modules/cpuUsage.c src/modules/cursor.c src/modules/custom.c + src/modules/command.c src/modules/date.c src/modules/datetime.c src/modules/de.c @@ -281,7 +284,7 @@ set(LIBFASTFETCH_SRC src/modules/poweradapter.c src/modules/processes.c src/modules/publicip.c - src/modules/resolution.c + src/modules/display.c src/modules/separator.c src/modules/shell.c src/modules/media.c @@ -300,6 +303,7 @@ set(LIBFASTFETCH_SRC src/util/FFlist.c src/util/FFstrbuf.c src/util/FFvaluestore.c + src/util/platform/FFPlatform.c ) if(LINUX) @@ -309,6 +313,7 @@ if(LINUX) src/detection/battery/battery_linux.c src/detection/bios/bios_linux.c src/detection/board/board_linux.c + src/detection/brightness/brightness_linux.c src/detection/chassis/chassis_linux.c src/detection/cpu/cpu_linux.c src/detection/cpuUsage/cpuUsage_linux.c @@ -340,14 +345,16 @@ if(LINUX) src/detection/users/users_linux.c src/detection/wifi/wifi_linux.c src/detection/wmtheme/wmtheme_linux.c + src/util/platform/FFPlatform_unix.c ) elseif(ANDROID) list(APPEND LIBFASTFETCH_SRC src/common/networking_linux.c src/common/processing_linux.c - src/detection/battery/battery_nosupport.c + src/detection/battery/battery_android.c src/detection/bios/bios_nosupport.c src/detection/board/board_nosupport.c + src/detection/brightness/brightness_nosupport.c src/detection/chassis/chassis_nosupport.c src/detection/cpu/cpu_linux.c src/detection/cursor/cursor_nosupport.c @@ -371,17 +378,19 @@ elseif(ANDROID) 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/wifi/wifi_android.c src/detection/wmtheme/wmtheme_nosupport.c + src/util/platform/FFPlatform_unix.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/battery/battery_bsd.c src/detection/bios/bios_bsd.c src/detection/board/board_nosupport.c + src/detection/brightness/brightness_nosupport.c src/detection/chassis/chassis_nosupport.c src/detection/cpu/cpu_bsd.c src/detection/cpuUsage/cpuUsage_bsd.c @@ -413,6 +422,7 @@ elseif(BSD) src/detection/users/users_linux.c src/detection/wifi/wifi_nosupport.c src/detection/wmtheme/wmtheme_linux.c + src/util/platform/FFPlatform_unix.c ) elseif(APPLE) list(APPEND LIBFASTFETCH_SRC @@ -422,6 +432,7 @@ elseif(APPLE) src/detection/battery/battery_apple.c src/detection/bios/bios_apple.c src/detection/board/board_nosupport.c + src/detection/brightness/brightness_apple.c src/detection/chassis/chassis_nosupport.c src/detection/cpu/cpu_apple.c src/detection/cpuUsage/cpuUsage_apple.c @@ -450,6 +461,7 @@ elseif(APPLE) src/detection/wmtheme/wmtheme_apple.m src/util/apple/cf_helpers.c src/util/apple/osascript.m + src/util/platform/FFPlatform_unix.c ) elseif(WIN32) list(APPEND LIBFASTFETCH_SRC @@ -458,6 +470,7 @@ elseif(WIN32) src/detection/battery/battery_windows.c src/detection/bios/bios_windows.c src/detection/board/board_windows.c + src/detection/brightness/brightness_windows.cpp src/detection/chassis/chassis_nosupport.c src/detection/cpu/cpu_windows.c src/detection/cpuUsage/cpuUsage_windows.c @@ -483,11 +496,10 @@ elseif(WIN32) 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 + src/util/platform/FFPlatform_windows.c ) endif() @@ -626,6 +638,10 @@ ff_lib_enable(LIBCJSON "libcjson" "CJson" ) +ff_lib_enable(LIBNM + "libnm" + "libnm" +) ff_lib_enable(FREETYPE "freetype2" "FreeType2" @@ -638,7 +654,6 @@ if(ENABLE_THREADS) endif() endif() - if(ENABLE_BUFFER) target_compile_definitions(libfastfetch PRIVATE FF_ENABLE_BUFFER) endif() @@ -652,6 +667,7 @@ if(APPLE) PRIVATE "-framework Cocoa" PRIVATE "-framework CoreWLAN" PRIVATE "-weak_framework MediaRemote -F /System/Library/PrivateFrameworks" + PRIVATE "-weak_framework DisplayServices -F /System/Library/PrivateFrameworks" ) elseif(WIN32) target_compile_definitions(libfastfetch PRIVATE -D_WIN32_WINNT=0x0601) @@ -712,6 +728,11 @@ target_link_libraries(flashfetch PRIVATE libfastfetch ) +if(ENABLE_BUFFER) # FF_ENABLE_BUFFER is used in fastfetch.c + target_compile_definitions(fastfetch PRIVATE FF_ENABLE_BUFFER) + target_compile_definitions(flashfetch PRIVATE FF_ENABLE_BUFFER) +endif() + if(WIN32) set(TARGET_NAME fastfetch) target_sources(fastfetch diff --git a/README.md b/README.md index 54d51b7ec..c1d5dbb99 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, FreeBSD, MacOS and Windows 7+ 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 mainly in C, with performance and customizability in mind. Currently, Linux, Android, FreeBSD, MacOS and Windows 7+ are supported. @@ -10,17 +10,21 @@ Fastfetch is a [neofetch](https://github.com/dylanaraps/neofetch)-like tool for +There are [screenshots on different platforms](https://github.com/LinusDierheimer/fastfetch/wiki) + ## Customization With customization and speed being two competing goals, this project actually builds two executables. -* The main one being `fastfetch`, which can be very greatly configured via flags. These flags can be made persistent in `~/.config/fastfetch/config.conf`. To view the available options run `fastfetch --help`. +* The main one being `fastfetch`, which can be very greatly configured via flags. These flags can be made persistent in `$XDG_CONFIG_HOME/fastfetch/config.conf`. To view the available options run `fastfetch --help`. * The second executable being built is called `flashfetch`, which is configured at compile time to eliminate any possible overhead. Configuration of it can be very easily done in [`src/flashfetch.c`](src/flashfetch.c). At the moment the performance difference is measurable, but too small to be human recognizable. But the leap will get bigger with more and more options coming, and on slow machines this might actually make a difference. There are some premade config files in [`presets`](presets), including the ones used for the screenshots above. You can load them using `--load-config `. They may also serve as a good example for format arguments. +Logos can be heavily customized to. See the [logo documentation](doc/logo.md) for more information. + ## Dependencies 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. @@ -34,8 +38,8 @@ The following libraries are used if present at runtime: * [`libxcb-randr`](https://xcb.freedesktop.org/), [`libXrandr`](https://gitlab.freedesktop.org/xorg/lib/libxrandr), [`libxcb`](https://xcb.freedesktop.org/), - [`libX11`](https://gitlab.freedesktop.org/xorg/lib/libx11): At least one of them sould be present in X11 sessions for better resolution detection and faster WM detection. The `*randr` ones provide multi monitor support The `libxcb*` ones usually have better performance. -* [`libwayland-client`](https://wayland.freedesktop.org/): Better resolution performance and output in wayland sessions. Supports different refresh rates per monitor. + [`libX11`](https://gitlab.freedesktop.org/xorg/lib/libx11): At least one of them sould be present in X11 sessions for better display detection and faster WM detection. The `*randr` ones provide multi monitor support The `libxcb*` ones usually have better performance. +* [`libwayland-client`](https://wayland.freedesktop.org/): Better display performance and output in wayland sessions. Supports different refresh rates per monitor. * [`libGIO`](https://developer.gnome.org/gio/unstable/): Needed for values that are only stored GSettings. * [`libDConf`](https://developer.gnome.org/dconf/unstable/): Needed for values that are only stored in DConf + Fallback for GSettings. * [`libmagickcore` (ImageMagick)](https://www.imagemagick.org/): Images in terminal using sixel or kitty graphics protocol. @@ -50,10 +54,12 @@ 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 ). +* [`libnm`](https://networkmanager.dev/docs/libnm/latest/): Used for Wifi detection on Linux. ### macOS * [`MediaRemote`](https://iphonedev.wiki/index.php/MediaRemote.framework): Need for Media detection. It's a private framework provided by newer macOS system. +* [`DisplayServices`](https://developer.apple.com/forums/thread/666383#663154022): Need for screen brightness 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) @@ -67,10 +73,7 @@ For the image logo, iTerm with iterm image protocol should work. Apple Terminal * [`libvulkan`](https://www.vulkan.org/): Vulkan module. Usually has been provided by GPU drivers. [`vulkan-loader`](https://github.com/msys2/MINGW-packages/tree/master/mingw-w64-vulkan-loader) [`vulkan-headers`](https://github.com/msys2/MINGW-packages/tree/master/mingw-w64-vulkan-headers) * [`libOpenCL`](https://www.khronos.org/opencl/): OpenCL module. [`opencl-icd`](https://github.com/msys2/MINGW-packages/tree/master/mingw-w64-opencl-icd) -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-win7` is provided to support these old systems, which - -1. Build with the ancient [MSVCRT](https://en.wikipedia.org/wiki/Microsoft_Windows_library_files#MSVCRT.DLL,_MSVCP*.DLL_and_CRTDLL.DLL) 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. +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, as fastfetch for Windows targets [UCRT](https://learn.microsoft.com/en-us/cpp/windows/universal-crt-deployment) C runtime library, [it must be installed manually](https://support.microsoft.com/en-us/topic/update-for-universal-c-runtime-in-windows-c0514201-7fe6-95a3-b0a5-287930f3560c) as UCRT is only pre-installed in Windows 10 and later. 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. @@ -78,43 +81,39 @@ For the image logo, only chafa is supported due to [the design flaw of ConPTY](h * [`freetype`](https://www.freetype.org/): Used for Termux font detection. [`freetype`](https://github.com/termux/termux-packages/tree/master/packages/freetype) * [`libvulkan`](https://www.vulkan.org/): Vulkan module, also used for GPU detection. Usually has been provided by Android system. [`vulkan-loader-android`](https://github.com/termux/termux-packages/tree/master/packages/vulkan-loader-android) [`vulkan-headers`](https://github.com/termux/termux-packages/tree/master/packages/vulkan-headers) +* [`termux-api`](https://github.com/termux/termux-api-package): Used for Wifi / Battery detection. Read [the official doc](https://wiki.termux.com/wiki/Termux:API) for detail or if you hang on these modules (IMPORTANT). [`termux-api`](https://github.com/termux/termux-packages/tree/master/packages/termux-api) ## Support status All categories not listed here should work without needing a specific implementation. ##### Available Modules ``` -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 +Battery, Bios, Board, Break, Brightness, Colors, Command, CPU, CPUUsage, Cursor, Custom, Date, DateTime, DE, Disk, Display, Font, GPU, Host, Icons, Kernel, Locale, LocalIP, Media, Memory, OpenCL, OpenGL, Packages, Player, Power Adapter, Processes, PublicIP, Separator OS, Shell, Swap, Terminal, Terminal Font, Theme, Time, Title, Uptime, Vulkan, Wifi, WM, WMTheme ``` -##### Logos +##### Builtin logos ``` -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 +AlmaLinux, Alpine, Android, Arch, Arco, Artix, Bedrock, CachyOS, CentOS, CRUX, Crystal, Debian, Deepin, Devuan, Endeavour, Enso, Fedora, FreeBSD, Garuda, Gentoo, KDE Neon, KISS, Kubuntu, LangitKetujuh, Linux, MacOS, Manjaro, Mint, MSYS2, NixOS, Nobara, OpenSUSE, OpenSUSE LEAP, OpenSUSE Tumbleweed, Parabola, Pop!_OS, Raspbian, RebornOS, RedstarOS, Rocky, Rosa, Slackware, Solus, Ubuntu, Vanilla, Void, Windows, Windows 11, Windows 8, 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 / iTerm graphics protocol or chafas image to text conversion. ##### Package managers ``` -Pacman, dpkg, rpm, emerge, eopkg, xbps, nix, Flatpak, Snap, apk, pkg, brew, MacPorts, scoop, Chocolatey +apk, brew, Chocolatey, dpkg, emerge, eopkg, Flatpak, MacPorts, nix, Pacman, pkg, rpm, scoop, Snap, xbps ``` ##### WM themes ``` -KWin, Mutter, Muffin, Marco, XFWM, Openbox (LXDE, LXQT & without DE), Quartz Compositor (macOS), DWM (Windows) +DWM (Windows), KWin, Marco, Muffin, Mutter, Openbox (LXDE, LXQT & without DE), Quartz Compositor (macOS), XFWM ``` ##### DE versions ``` -KDE Plasma, Gnome, Cinnamon, Mate, XFCE4, LXQt +Budgie, Cinnamon, Gnome, KDE Plasma, LXQt, Mate, XFCE4 ``` ##### Terminal fonts ``` -Konsole, Gnome Terminal, Tilix, XFCE4 Terminal, Alacritty, Kitty, LXTerminal, Deepin Terminal, iTerm2, Apple Terminal, Warp, TTY, Windows Terminal, Termux, mintty, ConEmu +Alacritty, Apple Terminal, ConEmu, Deepin Terminal, Gnome Terminal, iTerm2, Kitty, Konsole, LXTerminal, mintty, Termux, Tilix, TTY, Warp, WezTerm, Windows Terminal, XFCE4 Terminal ``` ## Building @@ -134,12 +133,13 @@ If the build process fails to find the headers for a library listed in [dependen Currently GCC or clang is required (MSVC is not supported). MSYS2 with CLANG64 subsystem (or CLANGARM64 if needed) is suggested (and tested) to build fastfetch. If you need Windows 7 / 8.x support, using MINGW64 is suggested. 1. Install [MSYS2](https://www.msys2.org/#installation) -1. Open `MSYS2 CLANG64` (not `MSYS2 / MSYS`, which targets cygwin C runtime) +1. Open `MSYS2 / CLANG64` (not `MSYS2 / MSYS`, which targets cygwin C runtime) 1. Install dependencies ```bash pacman -Syu mingw-w64-clang-x86_64-cmake mingw-w64-clang-x86_64-pkgconf 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 ``` -1. Follow building instructions of Linux + +Follow the building instructions of Linux next. ## Packaging diff --git a/completions/bash b/completions/bash index 10356ce85..7c32c4cc3 100644 --- a/completions/bash +++ b/completions/bash @@ -13,7 +13,7 @@ __fastfetch_complete_help() "processes-format" "packages-format" "shell-format" - "resolution-format" + "display-format" "de-format" "wm-format" "wm-theme-format" @@ -160,6 +160,8 @@ __fastfetch_completion() "--print-config-system" "--print-config-user" "--print-structure" + "--gen-config" + "--gen-config-force" ) local FF_OPTIONS_HELP=( @@ -187,8 +189,15 @@ __fastfetch_completion() "--pipe" "--title-fqdn" "--escape-bedrock" + "--shell-version" + "--terminal-version" + "--disk-folders" "--disk-show-removable" "--disk-show-hidden" + "--disk-show-subvolumes" + "--gpu-hide-integrated" + "--gpu-hide-discrete" + "--disk-show-unknown" ) local FF_OPTIONS_STRING=( @@ -196,6 +205,7 @@ __fastfetch_completion() "--logo-padding" "--logo-padding-left" "--logo-padding-right" + "--logo-padding-top" "--logo-color-1" "--logo-color-2" "--logo-color-3" @@ -243,9 +253,9 @@ __fastfetch_completion() "--shell-key" "--shell-format" "--shell-error" - "--resolution-key" - "--resolution-format" - "--resolution-error" + "--display-key" + "--display-format" + "--display-error" "--de-key" "--de-format" "--de-error" diff --git a/doc/logo.md b/doc/logo.md new file mode 100644 index 000000000..69d13e8ef --- /dev/null +++ b/doc/logo.md @@ -0,0 +1,116 @@ +# Logo options + +## General + +| Option | Default | Description | +|--------|---------|-------------| +| `--logo`, `-l` | `""` | The logo source, interpreted depending on type | +| `--logo-type` | `auto` | The logo type. See the following categories for possible values | +| `--` | `""` | Short for `--logo-type --logo ` | +| `--logo-padding` | `""` | Sets the padding left and right of the logo | +| `--logo-padding-left` | `0` | Sets the padding left of the logo | +| `--logo-padding-right` | `4` | Sets the padding right of the logo | +| `--logo-padding-top` | `0` | Sets the padding top of the logo | + +All filesytem paths can be: +* Absolute +* Relative to the current working directory +* Relative to any of the directories listed by `fastfetch --list-data-paths` + `/logos` + For example, the file `~/.local/share/fastfetch/logos/shrek` can simply be referenced as `shrek`. + +## Text + +| Option | Default | Description | +|--------|---------|-------------| +| `--logo-color-[1-9]` | `""` | Overrides a color for logos that support it | +| `--logo-print-remaining` | `true` | Print the remaining logo, if it is higher than the keys | + +* Color placeholders are in the form `$[1-9]`. To print a `$`, use `$$`. +* If a color placeholder value is not set, the placeholder is simply discarded. + +## Image + +| Option | Default | Description | +|--------|---------|-------------| +| `--logo-width` | `""` | Sets the width of the logo for logos that support it | +| `--logo-height` | `""` | Sets the height of the logo for logos that support it | + +* If one of the height / width options is set, but the other not, the aspect ratio is preserved. +* If neither height nor width is set, the original size is used. + +# Logo types + +## auto + +Trys to detect the logo type depending on the value of `--logo`, in the following order: +* If the value is empty / not set, an autodected builtin logo is displayed. +* If the value is the name of a builtin logo, it is displayed. +* If the value is the path to an image file, and the terminal emulator is known to support an image protocol, the image is displayed. +* If the value is the path to a text file, the content is displayed. +* The autodetected builtin logo is displayed. + +## builtin + +The value of `--logo` is interpreted as the name of a builtin logo. +* If the value is empty / not set, it is autodetected. +* Use `--list-logos` to get a list of all available logos. +* Use `--print-logos` to see all available logos. +* All builtin logos set default values for `--logo-color-[1-9]`. +* Use `none` to disable the logo, but keep key colors from the detected logo. + +## file + +The value of `--logo` is interpreted as the path to a file. The content of the file is displayed. +* If the file can't be read, the autodetected builtin logo is displayed. + +## file-raw + +The value of `--logo` is interpreted as the path to a file. The content of the file is displayed. +* No color placeholder replacement is done. +* If the file can't be read, the autodetected builtin logo is displayed. + +## data + +The value of `--logo` is interpreted as the logo and directly displayed. +* If the value is empty / not set, the autodetected builtin logo is displayed. + +## data-raw + +The value of `--logo` is interpreted as the logo and directly displayed. +* No color placeholder replacement is done. +* If the value is empty / not set, the autodetected builtin logo is displayed. + +## sixel + +The value of `--logo` is interpreted as the path to an image file. It is displayed using the sixel graphics protocol. +* `fastfetch` must be compiled with the `imagemagick6` or `imagemagick7` feature. + +## kitty + +The value of `--logo` is interpreted as the path to an image file. It is displayed using the kitty graphics protocol. +* If the file is a png file, and both `--logo-width` and `--logo-height` are specified, the image is directly send to the terminal emulator. +* Otherwise `fastfetch` must be compiled with the `imagemagick6` or `imagemagick7` feature. + +## iterm + +The value of `--logo` is interpreted as the path to an image file. It is displayed using the iTerm graphics protocol. +* Both `--logo-width` and `--logo-height` must be specified. + +## chafa + +The value of `--logo` is interpreted as the path to an image file. It is converted to an ascii logo using `libchafa`. +* `fastfetch` must be compiled with the `chafa` feature. +* `fastfetch` must be compiled with the `imagemagick6` or `imagemagick7` feature. +* Use `--chafa-fg-only` to set weather to render foreground only. +* Use `--chafa-symbols` to set the symbols. +* Use `--chafa-canvas-mode` to set the canvas mode. +* Use `--chafa-color-space` to set the color space. +* Use `--chafa-dither-mode` to set the dither mode. +* See the [chafa documentation](https://hpjansson.org/chafa/man/) for more information. + +## raw + +The value of `--logo` is interpreted as the path to a binary file. It is printed as-is. +* Both `--logo-width` and `--logo-height` must be specified. +* Use this to display a pre converted image file. +* If the file can't be read, the autodetected builtin logo is displayed. diff --git a/presets/all b/presets/all index 6f5fbb4d9..85be11eef 100644 --- a/presets/all +++ b/presets/all @@ -1 +1 @@ ---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:Media:PublicIP:LocalIP:Wifi:DateTime:Locale:Vulkan:OpenGL:OpenCL:Users:Weather:Break:Colors +--structure Title:Separator:OS:Host:Bios:Board:Chassis:Kernel:Uptime:Processes:Packages:Shell:Display:Brightness:DE:WM:WMTheme:Theme:Icons:Font:Cursor:Terminal:TerminalFont:CPU:CPUUsage:GPU:Memory:Swap:Disk:Battery:PowerAdapter:Player:Media:PublicIP:LocalIP:Wifi:DateTime:Locale:Vulkan:OpenGL:OpenCL:Users:Weather:Break:Colors diff --git a/presets/hardware b/presets/hardware index 3f3995d02..f93873197 100644 --- a/presets/hardware +++ b/presets/hardware @@ -1,4 +1,4 @@ ---structure Host:Resolution:CPU:GPU:Memory:Disk:Battery +--structure Host:Display:CPU:GPU:Memory:Disk:Battery --memory-format "{?2}{2}{?}" --disk-format "{?2}{2}{?}" --battery-format "{?1}{1} {?}{?2}{2} {?}{?3}({3}){?}" diff --git a/presets/neofetch b/presets/neofetch index 192abcf1a..9d42df956 100644 --- a/presets/neofetch +++ b/presets/neofetch @@ -1,10 +1,10 @@ ---structure "Title:Separator:OS:Host:Kernel:Uptime:Packages:Shell:Resolution:DE:WM:WMTheme:Theme:Icons:Terminal:TerminalFont:CPU:GPU:Memory:Break:Colors" +--structure "Title:Separator:OS:Host:Kernel:Uptime:Packages:Shell:Display:DE:WM:WMTheme:Theme:Icons:Terminal:TerminalFont:CPU:GPU:Memory:Break:Colors" --host-format "{/2}{-}{/}{2}{?3} {3}{?}" --os-format "{3} {12}" ---resolution-format "{}x{}" +--display-format "{}x{}" --de-format "{2} {3}" --theme-format "{?1}{1}{?3} {3}{?} [Plasma], {?}{7}" --terminal-font-format "{/2}{-}{/}{2}{?3} {3}{?}" --memory-format "{/1}{-}{/}{/2}{-}{/}{} / {}" --gpu-key "GPU" ---resolution-key "Resolution" +--display-key "Display" diff --git a/presets/paleofetch b/presets/paleofetch index fbaa17848..66913e3dc 100644 --- a/presets/paleofetch +++ b/presets/paleofetch @@ -1,5 +1,5 @@ ---structure Title:Separator:OS:Host:Kernel:Uptime:Battery:Break:Packages:Shell:Resolution:Terminal:Break:CPU:GPU:Memory:Break:Colors +--structure Title:Separator:OS:Host:Kernel:Uptime:Battery:Break:Packages:Shell:Display:Terminal:Break:CPU:GPU:Memory:Break:Colors --host-format "{/2}{-}{/}{2}{?3} {3}{?}" --battery-format "{/4}{-}{/}{4}%{?5} [{5}]{?}" ---resolution-key "Resolution" +--display-key "Display" --gpu-key "GPU" diff --git a/presets/verbose b/presets/verbose index 5a9f2f0e5..57711c43d 100644 --- a/presets/verbose +++ b/presets/verbose @@ -5,7 +5,7 @@ --processes-format Count: {} --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: {} +--display-format Width: {}; Height: {}; Refresh rate: {} --de-format Process name: {}; Pretty name: {}; Version: {} --wm-format Process name: {}; Pretty name: {}; Protocol: {} --wm-theme-format Name: {} diff --git a/src/common/bar.h b/src/common/bar.h index a6c2be234..575adc01d 100644 --- a/src/common/bar.h +++ b/src/common/bar.h @@ -9,6 +9,7 @@ enum { FF_PERCENTAGE_TYPE_NUM_BIT = 1, FF_PERCENTAGE_TYPE_BAR_BIT = 2, + FF_PERCENTAGE_TYPE_HIDE_OTHERS_BIT = 4, }; void ffAppendPercentBar(FFinstance* instance, FFstrbuf* buffer, uint8_t percent, uint8_t green, uint8_t yellow, uint8_t red); diff --git a/src/common/format.c b/src/common/format.c index b491713cf..1a4a688c5 100644 --- a/src/common/format.c +++ b/src/common/format.c @@ -19,6 +19,8 @@ void ffFormatAppendFormatArg(FFstrbuf* buffer, const FFformatarg* formatarg) 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_FLOAT) + ffStrbufAppendF(buffer, "%f", *(float*)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) diff --git a/src/common/format.h b/src/common/format.h index d860dbd29..cc7e29e6d 100644 --- a/src/common/format.h +++ b/src/common/format.h @@ -12,6 +12,7 @@ typedef enum FFformatargtype FF_FORMAT_ARG_TYPE_INT, FF_FORMAT_ARG_TYPE_STRING, FF_FORMAT_ARG_TYPE_STRBUF, + FF_FORMAT_ARG_TYPE_FLOAT, FF_FORMAT_ARG_TYPE_DOUBLE, FF_FORMAT_ARG_TYPE_LIST, FF_FORMAT_ARG_TYPE_BOOL diff --git a/src/common/init.c b/src/common/init.c index 37cf2f9aa..e4f106e94 100644 --- a/src/common/init.c +++ b/src/common/init.c @@ -12,107 +12,19 @@ #ifdef _WIN32 #include #include + #include + #include "util/windows/unicode.h" #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)) - { - FFstrbuf* buffer = (FFstrbuf*) ffListAdd(&state->configDirs); - ffStrbufInitA(buffer, 64); - ffStrbufAppendS(buffer, xdgConfigHome); - ffStrbufEnsureEndsWithC(buffer, '/'); - } - - #endif - - #define FF_ENSURE_ONLY_ONCE_IN_LIST(element) \ - 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; \ - } - - FFstrbuf* userConfigHome = ffListAdd(&state->configDirs); - ffStrbufInitA(userConfigHome, 64); - ffStrbufAppendS(userConfigHome, state->passwd->pw_dir); - ffStrbufAppendS(userConfigHome, "/.config/"); - FF_ENSURE_ONLY_ONCE_IN_LIST(userConfigHome) - - FFstrbuf* userHome = ffListAdd(&state->configDirs); - ffStrbufInitA(userHome, 64); - ffStrbufAppendS(userHome, state->passwd->pw_dir); - ffStrbufEnsureEndsWithC(userHome, '/'); - FF_ENSURE_ONLY_ONCE_IN_LIST(userHome) - - #if !(defined(_WIN32) || defined(__APPLE__) || defined(__ANDROID__)) - - FFstrbuf xdgConfigDirs; - ffStrbufInitA(&xdgConfigDirs, 64); - ffStrbufAppendS(&xdgConfigDirs, getenv("XDG_CONFIG_DIRS")); - - uint32_t startIndex = 0; - while (startIndex < xdgConfigDirs.length) - { - uint32_t colonIndex = ffStrbufNextIndexC(&xdgConfigDirs, startIndex, ':'); - xdgConfigDirs.chars[colonIndex] = '\0'; - - if(!ffStrSet(xdgConfigDirs.chars + startIndex)) - { - startIndex = colonIndex + 1; - continue; - } - - FFstrbuf* buffer = (FFstrbuf*) ffListAdd(&state->configDirs); - ffStrbufInitA(buffer, 64); - ffStrbufAppendS(buffer, xdgConfigDirs.chars + startIndex); - ffStrbufEnsureEndsWithC(buffer, '/'); - FF_ENSURE_ONLY_ONCE_IN_LIST(buffer); - - startIndex = colonIndex + 1; - } - ffStrbufDestroy(&xdgConfigDirs); - - FFstrbuf* systemConfigHome = ffListAdd(&state->configDirs); - ffStrbufInitA(systemConfigHome, 64); - 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"/"); - FF_ENSURE_ONLY_ONCE_IN_LIST(systemConfig) - - #undef FF_ENSURE_ONLY_ONCE_IN_LIST -} - 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; - #ifndef WIN32 - state->passwd = getpwuid(getuid()); - #else - state->passwd = ffGetPasswd(); - #endif - uname(&state->utsname); - initConfigDirs(state); + ffPlatformInit(&state->platform); } static void initModuleArg(FFModuleArgs* args) @@ -130,6 +42,7 @@ static void defaultConfig(FFinstance* instance) ffStrbufInit(&instance->config.logo.colors[i]); instance->config.logo.width = 0; instance->config.logo.height = 0; //preserve aspect ratio + instance->config.logo.paddingTop = 0; instance->config.logo.paddingLeft = 0; instance->config.logo.paddingRight = 4; instance->config.logo.printRemaining = true; @@ -163,13 +76,14 @@ static void defaultConfig(FFinstance* instance) initModuleArg(&instance->config.host); initModuleArg(&instance->config.bios); initModuleArg(&instance->config.board); + initModuleArg(&instance->config.brightness); initModuleArg(&instance->config.chassis); initModuleArg(&instance->config.kernel); initModuleArg(&instance->config.uptime); initModuleArg(&instance->config.processes); initModuleArg(&instance->config.packages); initModuleArg(&instance->config.shell); - initModuleArg(&instance->config.resolution); + initModuleArg(&instance->config.display); initModuleArg(&instance->config.de); initModuleArg(&instance->config.wm); initModuleArg(&instance->config.wmTheme); @@ -225,16 +139,25 @@ static void defaultConfig(FFinstance* instance) ffStrbufInitA(&instance->config.libcJSON, 0); ffStrbufInitA(&instance->config.libfreetype, 0); ffStrbufInit(&instance->config.libwlanapi); + ffStrbufInit(&instance->config.libnm); instance->config.cpuTemp = false; instance->config.gpuTemp = false; instance->config.batteryTemp = false; + instance->config.gpuHideIntegrated = false; + instance->config.gpuHideDiscrete = false; + + instance->config.shellVersion = true; + instance->config.terminalVersion = true; + instance->config.titleFQDN = false; ffStrbufInitA(&instance->config.diskFolders, 0); instance->config.diskShowRemovable = true; instance->config.diskShowHidden = false; + instance->config.diskShowUnknown = false; + instance->config.diskShowSubvolumes = false; ffStrbufInitA(&instance->config.batteryDir, 0); @@ -256,10 +179,27 @@ static void defaultConfig(FFinstance* instance) ffStrbufInitA(&instance->config.playerName, 0); instance->config.percentType = 1; + + ffStrbufInitS(&instance->config.commandShell, + #ifdef _WIN32 + "cmd" + #elif defined(__FreeBSD__) + "csh" + #else + "bash" + #endif + ); + ffListInit(&instance->config.commandKeys, sizeof(FFstrbuf)); + ffListInit(&instance->config.commandTexts, sizeof(FFstrbuf)); } void ffInitInstance(FFinstance* instance) { + #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 + initState(&instance->state); defaultConfig(instance); } @@ -308,6 +248,10 @@ static void resetConsole() if(ffHideCursor) fputs("\033[?25h", stdout); + + #if defined(_WIN32) && defined(FF_ENABLE_BUFFER) + fflush(stdout); + #endif } #ifdef _WIN32 @@ -401,7 +345,7 @@ static void destroyConfig(FFinstance* instance) destroyModuleArg(&instance->config.processes); destroyModuleArg(&instance->config.packages); destroyModuleArg(&instance->config.shell); - destroyModuleArg(&instance->config.resolution); + destroyModuleArg(&instance->config.display); destroyModuleArg(&instance->config.de); destroyModuleArg(&instance->config.wm); destroyModuleArg(&instance->config.wmTheme); @@ -457,6 +401,7 @@ static void destroyConfig(FFinstance* instance) ffStrbufDestroy(&instance->config.libcJSON); ffStrbufDestroy(&instance->config.libfreetype); ffStrbufDestroy(&instance->config.libwlanapi); + ffStrbufDestroy(&instance->config.libnm); ffStrbufDestroy(&instance->config.diskFolders); ffStrbufDestroy(&instance->config.batteryDir); @@ -466,13 +411,19 @@ static void destroyConfig(FFinstance* instance) ffStrbufDestroy(&instance->config.weatherOutputFormat); ffStrbufDestroy(&instance->config.osFile); ffStrbufDestroy(&instance->config.playerName); + + ffStrbufDestroy(&instance->config.commandShell); + FF_LIST_FOR_EACH(FFstrbuf, item, instance->config.commandKeys) + ffStrbufDestroy(item); + ffListDestroy(&instance->config.commandKeys); + FF_LIST_FOR_EACH(FFstrbuf, item, instance->config.commandTexts) + ffStrbufDestroy(item); + ffListDestroy(&instance->config.commandTexts); } 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); + ffPlatformDestroy(&instance->state.platform); } void ffDestroyInstance(FFinstance* instance) @@ -552,7 +503,10 @@ void ffListFeatures() "libcjson\n" #endif #ifdef FF_HAVE_FREETYPE - "freetype" + "freetype\n" + #endif + #ifdef FF_HAVE_LIBNM + "libnm\n" #endif "" , stdout); diff --git a/src/common/parsing.c b/src/common/parsing.c index 015235952..95bba4720 100644 --- a/src/common/parsing.c +++ b/src/common/parsing.c @@ -3,17 +3,6 @@ #include #include -bool ffStrSet(const char* str) -{ - if(str == NULL) - return false; - - while(isspace(*str)) - str++; - - return *str != '\0'; -} - void ffParseSemver(FFstrbuf* buffer, const FFstrbuf* major, const FFstrbuf* minor, const FFstrbuf* patch) { if(major->length > 0) diff --git a/src/common/parsing.h b/src/common/parsing.h index 239caa42f..613e20bb5 100644 --- a/src/common/parsing.h +++ b/src/common/parsing.h @@ -14,7 +14,6 @@ typedef struct FFVersion #define FF_VERSION_INIT ((FFVersion) {0}) -bool ffStrSet(const char* str); void ffParseSemver(FFstrbuf* buffer, const FFstrbuf* major, const FFstrbuf* minor, const FFstrbuf* patch); void ffParseGTK(FFstrbuf* buffer, const FFstrbuf* gtk2, const FFstrbuf* gtk3, const FFstrbuf* gtk4); diff --git a/src/common/printing.c b/src/common/printing.c index 4b593001a..f33d5cf33 100644 --- a/src/common/printing.c +++ b/src/common/printing.c @@ -130,9 +130,12 @@ void ffPrintColor(const FFstrbuf* colorValue) void ffPrintCharTimes(char c, uint32_t times) { + if(times == 0) + return; + 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)) + for(uint32_t i = sizeof(str); i <= times; i += (uint32_t)sizeof(str)) fwrite(str, 1, sizeof(str), stdout); uint32_t remaining = times % sizeof(str); if(remaining > 0) diff --git a/src/common/processing_linux.c b/src/common/processing_linux.c index d8bb4e0bd..a628aeb3d 100644 --- a/src/common/processing_linux.c +++ b/src/common/processing_linux.c @@ -31,8 +31,8 @@ const char* ffProcessAppendStdOut(FFstrbuf* buffer, char* const argv[]) //Parent close(pipes[1]); waitpid(childPid, NULL, 0); - ffAppendFDBuffer(pipes[0], buffer); + bool ok = ffAppendFDBuffer(pipes[0], buffer); close(pipes[0]); - return NULL; + return ok ? NULL : "ffAppendFDBuffer() failed"; } diff --git a/src/common/processing_windows.c b/src/common/processing_windows.c index 30e515734..736c121b3 100644 --- a/src/common/processing_windows.c +++ b/src/common/processing_windows.c @@ -4,7 +4,6 @@ #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 = { diff --git a/src/common/properties.c b/src/common/properties.c index 064d620aa..2e3246517 100644 --- a/src/common/properties.c +++ b/src/common/properties.c @@ -144,16 +144,11 @@ done: return true; } -bool ffParsePropFile(const char* filename, const char* start, FFstrbuf* buffer) -{ - return ffParsePropFileValues(filename, 1, (FFpropquery[]){{start, buffer}}); -} - bool ffParsePropFileHomeValues(const FFinstance* instance, const char* relativeFile, uint32_t numQueries, FFpropquery* queries) { FFstrbuf absolutePath; ffStrbufInitA(&absolutePath, 64); - ffStrbufAppendS(&absolutePath, instance->state.passwd->pw_dir); + ffStrbufAppend(&absolutePath, &instance->state.platform.homeDir); ffStrbufAppendC(&absolutePath, '/'); ffStrbufAppendS(&absolutePath, relativeFile); @@ -164,22 +159,17 @@ bool ffParsePropFileHomeValues(const FFinstance* instance, const char* relativeF return result; } -bool ffParsePropFileHome(const FFinstance* instance, const char* relativeFile, const char* start, FFstrbuf* buffer) -{ - return ffParsePropFileHomeValues(instance, relativeFile, 1, (FFpropquery[]){{start, buffer}}); -} - -bool ffParsePropFileConfigValues(const FFinstance* instance, const char* relativeFile, uint32_t numQueries, FFpropquery* queries) +bool ffParsePropFileListValues(const FFlist* list, const char* relativeFile, uint32_t numQueries, FFpropquery* queries) { bool foundAFile = false; FFstrbuf baseDir; ffStrbufInitA(&baseDir, 64); - for(uint32_t i = 0; i < instance->state.configDirs.length; i++) + for(uint32_t i = 0; i < list->length; i++) { - //We need to copy the config dir each time, because it used by multiple threads, so we can't directly write to it. - ffStrbufSet(&baseDir, ffListGet(&instance->state.configDirs, i)); + //We need to copy the dir each time, because it used by multiple threads, so we can't directly write to it. + ffStrbufSet(&baseDir, ffListGet(list, i)); ffStrbufAppendS(&baseDir, relativeFile); if(ffParsePropFileValues(baseDir.chars, numQueries, queries)) @@ -203,8 +193,3 @@ bool ffParsePropFileConfigValues(const FFinstance* instance, const char* relativ return foundAFile; } - -bool ffParsePropFileConfig(const FFinstance* instance, const char* relativeFile, const char* start, FFstrbuf* buffer) -{ - return ffParsePropFileConfigValues(instance, relativeFile, 1, (FFpropquery[]){{start, buffer}}); -} diff --git a/src/common/properties.h b/src/common/properties.h index dfdcad96e..ea78ed903 100644 --- a/src/common/properties.h +++ b/src/common/properties.h @@ -14,10 +14,42 @@ typedef struct FFpropquery bool ffParsePropLine(const char* line, const char* start, FFstrbuf* buffer); bool ffParsePropLines(const char* lines, const char* start, FFstrbuf* buffer); bool ffParsePropFileValues(const char* filename, uint32_t numQueries, FFpropquery* queries); -bool ffParsePropFile(const char* filename, const char* start, FFstrbuf* buffer); bool ffParsePropFileHomeValues(const FFinstance* instance, const char* relativeFile, uint32_t numQueries, FFpropquery* queries); -bool ffParsePropFileHome(const FFinstance* instance, const char* relativeFile, const char* start, FFstrbuf* buffer); -bool ffParsePropFileConfigValues(const FFinstance* instance, const char* relativeFile, uint32_t numQueries, FFpropquery* queries); -bool ffParsePropFileConfig(const FFinstance* instance, const char* relativeFile, const char* start, FFstrbuf* buffer); +bool ffParsePropFileListValues(const FFlist* list, const char* relativeFile, uint32_t numQueries, FFpropquery* queries); + +static inline bool ffParsePropFile(const char* filename, const char* start, FFstrbuf* buffer) +{ + return ffParsePropFileValues(filename, 1, (FFpropquery[]){{start, buffer}}); +} + +static inline bool ffParsePropFileHome(const FFinstance* instance, const char* relativeFile, const char* start, FFstrbuf* buffer) +{ + return ffParsePropFileHomeValues(instance, relativeFile, 1, (FFpropquery[]){{start, buffer}}); +} + +static inline bool ffParsePropFileList(const FFlist* list, const char* relativeFile, const char* start, FFstrbuf* buffer) +{ + return ffParsePropFileListValues(list, relativeFile, 1, (FFpropquery[]){{start, buffer}}); +} + +static inline bool ffParsePropFileConfigValues(const FFinstance* instance, const char* relativeFile, uint32_t numQueries, FFpropquery* queries) +{ + return ffParsePropFileListValues(&instance->state.platform.configDirs, relativeFile, numQueries, queries); +} + +static inline bool ffParsePropFileConfig(const FFinstance* instance, const char* relativeFile, const char* start, FFstrbuf* buffer) +{ + return ffParsePropFileConfigValues(instance, relativeFile, 1, (FFpropquery[]){{start, buffer}}); +} + +static inline bool ffParsePropFileDataValues(const FFinstance* instance, const char* relativeFile, uint32_t numQueries, FFpropquery* queries) +{ + return ffParsePropFileListValues(&instance->state.platform.dataDirs, relativeFile, numQueries, queries); +} + +static inline bool ffParsePropFileData(const FFinstance* instance, const char* relativeFile, const char* start, FFstrbuf* buffer) +{ + return ffParsePropFileDataValues(instance, relativeFile, 1, (FFpropquery[]){{start, buffer}}); +} #endif diff --git a/src/data/config_user.txt b/src/data/config_user.txt index 9725c0f81..0e987509e 100644 --- a/src/data/config_user.txt +++ b/src/data/config_user.txt @@ -6,7 +6,7 @@ # Empty lines or lines starting with # are ignored. # This file was shipped with $" FASTFETCH_PROJECT_VERSION $". -# Use fastfetch --print-config-user > ~/.config/fastfetch/config.conf to overwrite this file with the current defaults +# Use fastfetch --gen-config-force to overwrite this file with the current defaults # Below some often usefull options are listed. Uncomment and modify them so they take affect. # Note that there are a lot more options than the ones listed here, take a look at "fastfetch --help". @@ -16,7 +16,7 @@ # Load additional config files. # Some are shipped with fastfetch, list them with "fastfetch --list-presets". # Must be a path to a config file or the name of a shipped preset. -# The config file is completly loaded before continuing in the current file, so the placement of this option matters, as later options overwrite already set ones. +# The config file is completely loaded before continuing in the current file, so the placement of this option matters, as later options overwrite already set ones. # Can be used multiple times to load multiple config files / presets. #--load-config /path/to/config.txt @@ -114,6 +114,12 @@ # Default is 0. #--logo-padding-right 0 +# Logo padding top option: +# Adds a padding to the top side of the logo. +# Must be a positive integer. +# Default is 0. +#--logo-padding-top 0 + # Logo print remaining option: # Sets if the remaining logo should be printed, it is has more lines than modules to show. # Must be true or false. @@ -206,10 +212,45 @@ # Default is auto. #--gl auto +# GPU hide options +# Sets weather to hide certain gpu types +# Must be either true or false +# Default is false. +#--gpu-hide-integrated +#--gpu-hide-discrete + +# Shell option +# Sets if shell version should be detected and printed +# Must be either true or false +# Default is true. +#--shell-version true + +# Terminal option +# Sets if terminal version should be detected and printed +# Must be either true or false +# Default is true. +#--terminal-version true + +# Disk show options +# Sets if certain types of disk should be printed +# Must be either true or false +# Default is false except for --disk-show-removable. +#--disk-show-removable true +#--disk-show-hidden false +#--disk-show-subvolumes false +#--disk-show-unknown false + +# Disk option +# A colon (semicolon on Windows) separated list of folder paths for the disk output +# This option override `--disk-show-*` options above +# Must be a string +# Default is "/:/home" ("C:\\;D:\\ ..." on Windows). +#--disk-folders /:/home + # 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 +# 0: prints none; 1: prints percent number only; 2: prints bar only; 3: prints both percent number and bar; 6: prints bar and hide other texts #--percent-type 1 # Key options: @@ -223,7 +264,8 @@ #--processes-key Processes #--packages-key Packages #--shell-key Shell -#--resolution-key Resolution {1} +#--display-key Display {1} +#--brightness-key Brightness ({1}) #--de-key DE #--wm-key WM #--wm-theme-key WM Theme @@ -267,7 +309,8 @@ #--processes-format #--packages-format #--shell-format -#--resolution-format +#--display-format +#--brightness-format #--de-format #--wm-format #--wm-theme-format @@ -310,7 +353,8 @@ #--processes-error #--packages-error #--shell-error -#--resolution-error +#--display-error +#--brightness-error #--de-error #--wm-error #--wm-theme-error diff --git a/src/data/help.txt b/src/data/help.txt index dea266a24..b8a546c9c 100644 --- a/src/data/help.txt +++ b/src/data/help.txt @@ -4,6 +4,8 @@ Informative options: -h,--help: Show this message -h,--help : Show help for a specific command -v,--version: Show the version of fastfetch + --list-config-paths: List search paths of config files + --list-data-paths: List search paths of presets and logos --list-logos: List available logos --list-modules: List available modules --list-presets: List presets fastfetch knows about; they can be loaded with --load-config (+) @@ -12,6 +14,8 @@ Informative options: --print-config-system: Print the default system config --print-config-user: Print the default user config --print-structure: Print the default structure + --gen-config: Generate a sample config file in the default path + --gen-config-force: Generate a sample config file in the default path. Overwrite the existing one General options: --load-config : Load a config file or preset (+) @@ -31,6 +35,7 @@ Logo options: --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-padding-top : Set the padding on the top 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 @@ -95,27 +100,36 @@ Library options: Set the path of a library to load --lib-cjson Module specific options: - --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 + --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 + --shell-version : Set if shell version should be detected and printed + --terminal-version : Set if terminal version should be detected and printed + --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 + --disk-show-subvolumes : Set if subvolumes should be printed. Default is false + --disk-show-unknown : Set if unknown (unable to detect sizes) 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 + --gpu-hide-integrated : Hide integrated GPU if supported. Default is false + --gpu-hide-discrete : Hide discrete GPU 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, 6 for bar only. Default is 1 + --command-shell : Set the shell program to execute the command text. Default is cmd for Windows, csh for FreeBSD, bash for others + --command-key : Set the module key to display, can be specified mulitple times + --command-text : Set the command text to be executed, can be specified mulitple times 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/help_format.txt b/src/data/help_format.txt index 87a4b1cc2..106a7acdc 100644 --- a/src/data/help_format.txt +++ b/src/data/help_format.txt @@ -33,4 +33,4 @@ You can therefore use --host-format " " to disable host output. Note that --host-format "" would evaluate as not set, and therefore use the built-in host format Format string is also the way to go to set a fixed value - just use one without placeholders. -For example when running in headless mode, you could use "--resolution-format "Preferred". +For example when running in headless mode, you could use "--display-format Preferred". diff --git a/src/data/modules.txt b/src/data/modules.txt index a684c1b84..586c2de1d 100644 --- a/src/data/modules.txt +++ b/src/data/modules.txt @@ -11,6 +11,7 @@ Date Datetime DE Disk +Display Font GPU Host @@ -27,7 +28,6 @@ Player PowerAdapter Processes PublicIP -Resolution Separator Shell Swap diff --git a/src/data/structure.txt b/src/data/structure.txt index e47d27847..851657c6d 100644 --- a/src/data/structure.txt +++ b/src/data/structure.txt @@ -1 +1 @@ -Title:Separator:OS:Host:Kernel:Uptime:Packages:Shell:Resolution:DE:WM:WMTheme:Theme:Icons:Font:Cursor:Terminal:TerminalFont:CPU:GPU:Memory:Disk:Battery:PowerAdapter:Locale:Break:Colors +Title:Separator:OS:Host:Kernel:Uptime:Packages:Shell:Display:DE:WM:WMTheme:Theme:Icons:Font:Cursor:Terminal:TerminalFont:CPU:GPU:Memory:Disk:Battery:PowerAdapter:Locale:Break:Colors diff --git a/src/detection/battery/battery_android.c b/src/detection/battery/battery_android.c new file mode 100644 index 000000000..0c4772c3e --- /dev/null +++ b/src/detection/battery/battery_android.c @@ -0,0 +1,38 @@ +#include "fastfetch.h" +#include "battery.h" + +#include "common/processing.h" +#include "common/properties.h" + +const char* ffDetectBatteryImpl(FF_MAYBE_UNUSED FFinstance* instance, FFlist* results) +{ + FF_STRBUF_AUTO_DESTROY buffer; + ffStrbufInit(&buffer); + + if(ffProcessAppendStdOut(&buffer, (char* const[]){ + FASTFETCH_TARGET_DIR_ROOT "/libexec/termux-api", + "BatteryStatus" + })) + return "Starting `" FASTFETCH_TARGET_DIR_ROOT "/libexec/termux-api" " BatteryStatus` failed"; + + BatteryResult* battery = ffListAdd(results); + battery->temperature = FF_BATTERY_TEMP_UNSET; + ffStrbufInit(&battery->manufacturer); + ffStrbufInit(&battery->modelName); + ffStrbufInit(&battery->status); + ffStrbufInit(&battery->technology); + + if(ffParsePropLines(buffer.chars, "\"percentage\": ", &battery->status)) + { + battery->capacity = ffStrbufToDouble(&battery->status); + ffStrbufClear(&battery->status); + } + + if(ffParsePropLines(buffer.chars, "\"status\": ", &battery->status)) + { + ffStrbufTrimRight(&battery->status, ','); + ffStrbufTrim(&battery->status, '"'); + } + + return NULL; +} diff --git a/src/detection/battery/battery_bsd.c b/src/detection/battery/battery_bsd.c new file mode 100644 index 000000000..2cfbe4b7b --- /dev/null +++ b/src/detection/battery/battery_bsd.c @@ -0,0 +1,59 @@ +#include "fastfetch.h" +#include "battery.h" + +#include +#include +#include +#include + +const char* ffDetectBatteryImpl(FF_MAYBE_UNUSED FFinstance* instance, FFlist* results) +{ + //https://www.freebsd.org/cgi/man.cgi?acpi_battery(4) + //https://gitlab.xfce.org/panel-plugins/xfce4-battery-plugin/-/blob/master/panel-plugin/libacpi.c + + int acpifd = open("/dev/acpi", O_RDONLY); + if(acpifd < 0) + return "open(\"/dev/acpi\", O_RDONLY) failed"; + + int units = ioctl(acpifd, ACPIIO_BATT_GET_UNITS, 0); + if(units < 0) + { + close(acpifd); + return "No acpiio battery units found"; + } + + for(int i = 0; i < units; ++i) + { + union acpi_battery_ioctl_arg battio; + battio.unit = i; + + if(ioctl(acpifd, ACPIIO_BATT_GET_BATTINFO, &battio) < 0 || (battio.battinfo.state & ACPI_BATT_STAT_NOT_PRESENT)) + continue; + + BatteryResult* battery = ffListAdd(results); + battery->temperature = FF_BATTERY_TEMP_UNSET; + ffStrbufInit(&battery->manufacturer); + ffStrbufInit(&battery->modelName); + ffStrbufInit(&battery->status); + ffStrbufInit(&battery->technology); + + battery->capacity = battio.battinfo.cap; + if(battio.battinfo.state & ACPI_BATT_STAT_INVALID) + { + ffStrbufAppendS(&battery->status, "Invalid"); + } + else + { + if(battio.battinfo.state & ACPI_BATT_STAT_DISCHARG) + ffStrbufAppendS(&battery->status, "Discharging, "); + else if(battio.battinfo.state & ACPI_BATT_STAT_CHARGING) + ffStrbufAppendS(&battery->status, "Charging, "); + if(battio.battinfo.state & ACPI_BATT_STAT_CRITICAL) + ffStrbufAppendS(&battery->status, "Ctritical"); + ffStrbufTrimRight(&battery->status, ' '); + ffStrbufTrimRight(&battery->status, ','); + } + } + close(acpifd); + return NULL; +} diff --git a/src/detection/battery/battery_linux.c b/src/detection/battery/battery_linux.c index 6fe799eb0..756dd9c8a 100644 --- a/src/detection/battery/battery_linux.c +++ b/src/detection/battery/battery_linux.c @@ -99,6 +99,9 @@ const char* ffDetectBatteryImpl(FFinstance* instance, FFlist* results) { struct dirent* entry; while((entry = readdir(dirp)) != NULL) { + if(strcmp(entry->d_name, ".") == 0 || strcmp(entry->d_name, "..") == 0) + continue; + ffStrbufAppendS(&baseDir, entry->d_name); parseBattery(&baseDir, results); ffStrbufSubstrBefore(&baseDir, baseDirLength); diff --git a/src/detection/brightness/brightness.h b/src/detection/brightness/brightness.h new file mode 100644 index 000000000..d65843947 --- /dev/null +++ b/src/detection/brightness/brightness.h @@ -0,0 +1,17 @@ +#pragma once + +#ifndef FF_INCLUDED_detection_brightness_brightness +#define FF_INCLUDED_detection_brightness_brightness + +#include "fastfetch.h" +#include "util/FFlist.h" + +typedef struct FFBrightnessResult +{ + FFstrbuf name; + float value; +} FFBrightnessResult; + +const char* ffDetectBrightness(FFlist* result); // list of FFBrightnessResult + +#endif diff --git a/src/detection/brightness/brightness_apple.c b/src/detection/brightness/brightness_apple.c new file mode 100644 index 000000000..e3a93a5ea --- /dev/null +++ b/src/detection/brightness/brightness_apple.c @@ -0,0 +1,33 @@ +#include "brightness.h" + +#include + +extern int DisplayServicesGetBrightness(CGDirectDisplayID display, float *brightness) __attribute__((weak_import)); + +const char* ffDetectBrightness(FFlist* result) +{ + if(DisplayServicesGetBrightness == NULL) + return "DisplayServices function DisplayServicesGetBrightness is not available"; + + CGDirectDisplayID screens[128]; + uint32_t screenCount; + if(CGGetOnlineDisplayList(sizeof(screens) / sizeof(screens[0]), screens, &screenCount) != kCGErrorSuccess) + return "CGGetOnlineDisplayList() failed"; + + for(uint32_t i = 0; i < screenCount; i++) + { + CGDirectDisplayID screen = screens[i]; + float brightness; + if(DisplayServicesGetBrightness(screen, &brightness) != kCGErrorSuccess) + continue; + + FFBrightnessResult* display = (FFBrightnessResult*) ffListAdd(result); + if(CGDisplayIsBuiltin(screen)) + ffStrbufInitS(&display->name, "built-in"); + else + ffStrbufInitF(&display->name, "external-%u", (unsigned) screen); + display->value = brightness * 100; + } + + return NULL; +} diff --git a/src/detection/brightness/brightness_linux.c b/src/detection/brightness/brightness_linux.c new file mode 100644 index 000000000..0f2a9d5fd --- /dev/null +++ b/src/detection/brightness/brightness_linux.c @@ -0,0 +1,55 @@ +#include "brightness.h" +#include "common/io.h" + +#include + +const char* ffDetectBrightness(FF_MAYBE_UNUSED FFlist* result) +{ + //https://www.kernel.org/doc/Documentation/ABI/stable/sysfs-class-backlight + const char* backlightDirPath = "/sys/class/backlight/"; + + DIR* dirp = opendir(backlightDirPath); + if(dirp == NULL) + return "Failed to open `/sys/class/backlight/`"; + + FFstrbuf backlightDir; + ffStrbufInitA(&backlightDir, 64); + ffStrbufAppendS(&backlightDir, backlightDirPath); + + uint32_t backlightDirLength = backlightDir.length; + + FFstrbuf buffer; + ffStrbufInit(&buffer); + + struct dirent* entry; + while((entry = readdir(dirp)) != NULL) + { + if(strcmp(entry->d_name, ".") == 0 || strcmp(entry->d_name, "..") == 0) + continue; + + ffStrbufAppendS(&backlightDir, entry->d_name); + ffStrbufAppendS(&backlightDir, "/actual_brightness"); + if(ffReadFileBuffer(backlightDir.chars, &buffer)) + { + double actualBrightness = ffStrbufToDouble(&buffer); + ffStrbufSubstrBefore(&backlightDir, backlightDirLength); + ffStrbufAppendS(&backlightDir, entry->d_name); + ffStrbufAppendS(&backlightDir, "/max_brightness"); + if(ffReadFileBuffer(backlightDir.chars, &buffer)) + { + FFBrightnessResult* display = (FFBrightnessResult*) ffListAdd(result); + ffStrbufInitS(&display->name, entry->d_name); + double maxBrightness = ffStrbufToDouble(&buffer); + display->value = (float) (actualBrightness * 100 / maxBrightness); + } + + break; + } + } + + closedir(dirp); + ffStrbufDestroy(&backlightDir); + ffStrbufDestroy(&buffer); + + return NULL; +} diff --git a/src/detection/brightness/brightness_nosupport.c b/src/detection/brightness/brightness_nosupport.c new file mode 100644 index 000000000..4bd83c4de --- /dev/null +++ b/src/detection/brightness/brightness_nosupport.c @@ -0,0 +1,6 @@ +#include "brightness.h" + +const char* ffDetectBrightness(FF_MAYBE_UNUSED FFlist* result) +{ + return "Not supported on this platform"; +} diff --git a/src/detection/brightness/brightness_windows.cpp b/src/detection/brightness/brightness_windows.cpp new file mode 100644 index 000000000..3139fef4c --- /dev/null +++ b/src/detection/brightness/brightness_windows.cpp @@ -0,0 +1,27 @@ +extern "C" +{ +#include "brightness.h" +} +#include "util/windows/wmi.hpp" + +extern "C" +const char* ffDetectBrightness(FFlist* result) +{ + FFWmiQuery query(L"SELECT CurrentBrightness, InstanceName FROM WmiMonitorBrightness WHERE Active = true", nullptr, FFWmiNamespace::WMI); + if(!query) + return "Query WMI service failed"; + + while(FFWmiRecord record = query.next()) + { + FFBrightnessResult* display = (FFBrightnessResult*) ffListAdd(result); + ffStrbufInit(&display->name); + record.getString(L"InstanceName", &display->name); + ffStrbufSubstrAfterFirstC(&display->name, '\\'); + ffStrbufSubstrBeforeFirstC(&display->name, '\\'); + + uint64_t brightness; + record.getUnsigned(L"CurrentBrightness", &brightness); + display->value = (float) brightness; + } + return NULL; +} diff --git a/src/detection/cpu/cpu_linux.c b/src/detection/cpu/cpu_linux.c index 11b5ec965..73ce6eaca 100644 --- a/src/detection/cpu/cpu_linux.c +++ b/src/detection/cpu/cpu_linux.c @@ -7,7 +7,7 @@ #include #include -static void parseCpuInfo(FFCPUResult* cpu, FFstrbuf* physicalCoresBuffer, FFstrbuf* cpuMHz) +static void parseCpuInfo(FFCPUResult* cpu, FFstrbuf* physicalCoresBuffer, FFstrbuf* cpuMHz, FFstrbuf* cpuIsa, FFstrbuf* cpuUarch) { FILE* cpuinfo = fopen("/proc/cpuinfo", "r"); if(cpuinfo == NULL) @@ -19,7 +19,7 @@ static void parseCpuInfo(FFCPUResult* cpu, FFstrbuf* physicalCoresBuffer, FFstrb while(getline(&line, &len, cpuinfo) != -1) { //Stop after the first CPU - if(cpu->name.length > 0 && (*line == '\0' || *line == '\n')) + if(*line == '\0' || *line == '\n') break; (void)( @@ -27,6 +27,8 @@ static void parseCpuInfo(FFCPUResult* cpu, FFstrbuf* physicalCoresBuffer, FFstrb ffParsePropLine(line, "vendor_id :", &cpu->vendor) || ffParsePropLine(line, "cpu cores :", physicalCoresBuffer) || ffParsePropLine(line, "cpu MHz :", cpuMHz) || + ffParsePropLine(line, "isa :", cpuIsa) || + ffParsePropLine(line, "uarch :", cpuUarch) || (cpu->name.length == 0 && ffParsePropLine(line, "Hardware :", &cpu->name)) //For Android devices ); } @@ -65,9 +67,9 @@ static double getFrequency(const char* info, const char* scaling) return getGHz(scaling); } -static double detectCPUTemp(const FFinstance* instance) +static double detectCPUTemp(void) { - const FFTempsResult* temps = ffDetectTemps(instance); + const FFTempsResult* temps = ffDetectTemps(); for(uint32_t i = 0; i < temps->values.length; i++) { @@ -83,10 +85,29 @@ static double detectCPUTemp(const FFinstance* instance) return FF_CPU_TEMP_UNSET; } +static void parseIsa(FFstrbuf* cpuIsa) +{ + if(ffStrbufStartsWithS(cpuIsa, "rv")) + { + // RISC-V ISA string example: "rv64imafdch_zicsr_zifencei". + // The _z parts are not important for CPU showcasing, so we remove them. + if(ffStrbufContainC(cpuIsa, '_')) + ffStrbufSubstrBeforeFirstC(cpuIsa, '_'); + // Then we replace "imafd" with "g" since "g" is a shorthand. + if(ffStrbufContainS(cpuIsa, "imafd")) + { + // Remove 4 of the 5 characters and replace the remaining one with "g". + ffStrbufRemoveSubstr(cpuIsa, 4, 8); + cpuIsa->chars[4] = 'g'; + } + // The final ISA output of the above example is "rv64gch". + } +} + void ffDetectCPUImpl(const FFinstance* instance, FFCPUResult* cpu) { if(instance->config.cpuTemp) - cpu->temperature = detectCPUTemp(instance); + cpu->temperature = detectCPUTemp(); else cpu->temperature = FF_CPU_TEMP_UNSET; @@ -96,7 +117,13 @@ void ffDetectCPUImpl(const FFinstance* instance, FFCPUResult* cpu) FFstrbuf cpuMHz; ffStrbufInit(&cpuMHz); - parseCpuInfo(cpu, &physicalCoresBuffer, &cpuMHz); + FFstrbuf cpuIsa; + ffStrbufInit(&cpuIsa); + + FFstrbuf cpuUarch; + ffStrbufInit(&cpuUarch); + + parseCpuInfo(cpu, &physicalCoresBuffer, &cpuMHz, &cpuIsa, &cpuUarch); cpu->coresPhysical = ffStrbufToUInt16(&physicalCoresBuffer, 1); @@ -114,6 +141,23 @@ void ffDetectCPUImpl(const FFinstance* instance, FFCPUResult* cpu) cpu->frequencyMin = cpu->frequencyMax = ffStrbufToDouble(&cpuMHz) / 1000; } + if(cpuUarch.length > 0) + { + if(cpu->name.length > 0) + ffStrbufAppendC(&cpu->name, ' '); + ffStrbufAppend(&cpu->name, &cpuUarch); + } + + if(cpuIsa.length > 0) + { + parseIsa(&cpuIsa); + if(cpu->name.length > 0) + ffStrbufAppendC(&cpu->name, ' '); + ffStrbufAppend(&cpu->name, &cpuIsa); + } + ffStrbufDestroy(&physicalCoresBuffer); ffStrbufDestroy(&cpuMHz); + ffStrbufDestroy(&cpuIsa); + ffStrbufDestroy(&cpuUarch); } diff --git a/src/detection/cursor/cursor_linux.c b/src/detection/cursor/cursor_linux.c index a8dfb8810..63507e803 100644 --- a/src/detection/cursor/cursor_linux.c +++ b/src/detection/cursor/cursor_linux.c @@ -8,7 +8,7 @@ #include -static void detectCursorGTK(const FFinstance* instance, FFCursorResult* result) +static bool detectCursorGTK(const FFinstance* instance, FFCursorResult* result) { const FFGTKResult* gtk = ffDetectGTK4(instance); @@ -19,25 +19,11 @@ static void detectCursorGTK(const FFinstance* instance, FFCursorResult* result) gtk = ffDetectGTK2(instance); if(gtk->cursor.length == 0) - { - ffStrbufAppendS(&result->error, "Couldn't detect GTK Cursor"); - return; - } + return false; 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); + return true; } static void detectCursorFromConfigFile(const FFinstance* instance, const char* relativeFilePath, const char* themeStart, const char* themeDefault, const char* sizeStart, const char* sizeDefault, FFCursorResult* result) @@ -68,16 +54,6 @@ static bool detectCursorFromXResources(const FFinstance* instance, FFCursorResul 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); @@ -97,35 +73,18 @@ 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) - { + else 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); + else if(ffStrbufIgnCaseCompS(&wmde->dePrettyName, "KDE Plasma") == 0) + detectCursorFromConfigFile(instance, "kcminputrc", "cursorTheme =", "Breeze", "cursorSize =", "24", result); + else if(ffStrbufStartsWithIgnCaseS(&wmde->dePrettyName, "LXQt")) + detectCursorFromConfigFile(instance, "lxqt/session.conf", "cursor_theme =", "Adwaita", "cursor_size =", "24", result); + else if( + !detectCursorGTK(instance, result) && + !detectCursorFromEnv(instance, result) && + !ffParsePropFileHome(instance, ".icons/default/index.theme", "Inherits =", &result->theme) && + !detectCursorFromXResources(instance, result) && + !ffParsePropFileData(instance, "icons/default/index.theme", "Inherits =", &result->theme) + ) ffStrbufAppendS(&result->error, "Couldn't find cursor"); } diff --git a/src/detection/disk/disk.h b/src/detection/disk/disk.h index c061ce6c9..f4230ce46 100644 --- a/src/detection/disk/disk.h +++ b/src/detection/disk/disk.h @@ -9,7 +9,8 @@ typedef enum FFDiskType { FF_DISK_TYPE_REGULAR, FF_DISK_TYPE_HIDDEN, - FF_DISK_TYPE_EXTERNAL + FF_DISK_TYPE_EXTERNAL, + FF_DISK_TYPE_SUBVOLUME } FFDiskType; typedef struct FFDisk diff --git a/src/detection/disk/disk_linux.c b/src/detection/disk/disk_linux.c index 72d6c3f27..ec7a26abf 100644 --- a/src/detection/disk/disk_linux.c +++ b/src/detection/disk/disk_linux.c @@ -2,30 +2,181 @@ #include #include +#include +#include #include -static void strbufAppendMountPoint(FFstrbuf* mountpoint, const char* source) +#ifdef __USE_LARGEFILE64 + #define stat stat64 + #define statvfs statvfs64 + #define dirent dirent64 + #define readdir readdir64 +#endif + +static bool isPhysicalDevice(const char* device) { - while(*source != '\0' && !isspace(*source)) + //DrvFs is a filesystem plugin to WSL that was designed to support interop between WSL and the Windows filesystem. + if(strcmp(device, "drvfs") == 0) + return true; + + //Pseudo filesystems don't have a device in /dev + const char* devPrefix = "/dev/"; + if(strncmp(device, devPrefix, strlen(devPrefix)) != 0) + return false; + + //Skip /dev/ prefix + device += strlen(devPrefix); + + if( + strncmp(device, "loop", 4) == 0 || //Ignore loop devices + strncmp(device, "ram", 3) == 0 || //Ignore ram devices + strncmp(device, "fd", 2) == 0 //Ignore fd devices + ) return false; + + return true; +} + +static void appendNextEntry(FFstrbuf* buffer, char** source) +{ + //Read the current entry into the buffer + while(**source != '\0' && !isspace(**source)) { //After a backslash the next 3 characters are octal ascii codes - if(*source == '\\' && strnlen(source, 4) == 4) + if(**source == '\\' && strnlen(*source, 4) == 4) { char octal[4] = {0}; - strncpy(octal, source + 1, 3); + strncpy(octal, *source + 1, 3); long value = strtol(octal, NULL, 8); //Returns 0 on error, so no need to check endptr if(value > 0 && value < CHAR_MAX) { - ffStrbufAppendC(mountpoint, (char) value); - source += 4; + ffStrbufAppendC(buffer, (char) value); + *source += 4; continue; } } - ffStrbufAppendC(mountpoint, *source); - ++source; + ffStrbufAppendC(buffer, **source); + ++*source; } + + //Skip whitespace + while(isspace(**source)) + ++*source; +} + +static void detectNameFromPath(FFDisk* disk, const struct stat* deviceStat, FFstrbuf* basePath) +{ + DIR* dir = opendir(basePath->chars); + if(dir == NULL) + return; + + uint32_t basePathLength = basePath->length; + + struct dirent* entry; + while((entry = readdir(dir)) != NULL) + { + if(entry->d_name[0] == '.') + continue; + + ffStrbufAppendS(basePath, entry->d_name); + + struct stat entryStat; + bool ret = stat(basePath->chars, &entryStat) == 0; + + ffStrbufSubstrBefore(basePath, basePathLength); + + if(!ret || deviceStat->st_ino != entryStat.st_ino) + continue; + + ffStrbufAppendS(&disk->name, entry->d_name); + break; + } + + closedir(dir); +} + +static void detectName(FFDisk* disk, const FFstrbuf* device) +{ + struct stat deviceStat; + if(stat(device->chars, &deviceStat) != 0) + return; + + FFstrbuf basePath; + ffStrbufInit(&basePath); + + //Try partlabel first + ffStrbufSetS(&basePath, "/dev/disk/by-partlabel/"); + detectNameFromPath(disk, &deviceStat, &basePath); + + //Try label second + if(disk->name.length == 0) + { + ffStrbufSetS(&basePath, "/dev/disk/by-label/"); + detectNameFromPath(disk, &deviceStat, &basePath); + } + + //Use the mountpoint as a last resort + if(disk->name.length == 0) + ffStrbufAppend(&disk->name, &disk->mountpoint); + + ffStrbufDestroy(&basePath); +} + +#ifdef __ANDROID__ + +static void detectType(FF_MAYBE_UNUSED const FFlist* devices, FFDisk* currentDisk, FF_MAYBE_UNUSED const char* options) +{ + if(ffStrbufEqualS(¤tDisk->mountpoint, "/") || ffStrbufEqualS(¤tDisk->mountpoint, "/storage/emulated")) + currentDisk->type = FF_DISK_TYPE_REGULAR; + else if(ffStrbufStartsWithS(¤tDisk->mountpoint, "/mnt/media_rw/")) + currentDisk->type = FF_DISK_TYPE_EXTERNAL; + else + currentDisk->type = FF_DISK_TYPE_HIDDEN; +} + +#else + +static bool isSubvolume(const FFlist* devices) +{ + const FFstrbuf* currentDevie = ffListGet(devices, devices->length - 1); + + for(uint32_t i = 0; i < devices->length - 1; i++) + { + const FFstrbuf* otherDevice = ffListGet(devices, i); + + if(ffStrbufEqual(currentDevie, otherDevice)) + return true; + } + + return false; +} + +static void detectType(const FFlist* devices, FFDisk* currentDisk, const char* options) +{ + if(isSubvolume(devices)) + currentDisk->type = FF_DISK_TYPE_SUBVOLUME; + else if(strstr(options, "nosuid") != NULL || strstr(options, "nodev") != NULL) + currentDisk->type = FF_DISK_TYPE_EXTERNAL; + else if(ffStrbufStartsWithS(¤tDisk->mountpoint, "/boot") || ffStrbufStartsWithS(¤tDisk->mountpoint, "/efi")) + currentDisk->type = FF_DISK_TYPE_HIDDEN; + else + currentDisk->type = FF_DISK_TYPE_REGULAR; +} + +#endif + +static void detectStats(FFDisk* disk) +{ + 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 + + 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); } void ffDetectDisksImpl(FFDiskResult* disks) @@ -37,88 +188,53 @@ void ffDetectDisksImpl(FFDiskResult* disks) return; } + FFlist devices; + ffListInit(&devices, sizeof(FFstrbuf)); + char* line = NULL; size_t len = 0; while(getline(&line, &len, mountsFile) != EOF) { + if(!isPhysicalDevice(line)) + continue; + + //We have a valid device, add it to the list + FFDisk* disk = ffListAdd(&disks->disks); + //Format of the file: " ..." (Same as fstab) char* currentPos = line; - //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; + //detect device + FFstrbuf* device = ffListAdd(&devices); + ffStrbufInit(device); + appendNextEntry(device, ¤tPos); - //Skip /dev/ or drvfs - currentPos += 5; + //detect mountpoint + ffStrbufInit(&disk->mountpoint); + appendNextEntry(&disk->mountpoint, ¤tPos); - //Don't show loop file systems - if(strncasecmp(currentPos, "loop", 4) == 0) - continue; + //detect filesystem + ffStrbufInit(&disk->filesystem); + appendNextEntry(&disk->filesystem, ¤tPos); - FFDisk* disk = ffListAdd(&disks->disks); + //detect name + ffStrbufInit(&disk->name); + detectName(disk, device); - //Go to mountpoint - while(!isspace(*currentPos) && *currentPos != '\0') - ++currentPos; - while(isspace(*currentPos)) - ++currentPos; - - ffStrbufInitA(&disk->mountpoint, 16); - strbufAppendMountPoint(&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 + //detect type + detectType(&devices, disk, currentPos); //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 + detectStats(disk); } if(line != NULL) free(line); + FF_LIST_FOR_EACH(FFstrbuf, device, devices) + ffStrbufDestroy(device); + ffListDestroy(&devices); + fclose(mountsFile); } diff --git a/src/detection/displayserver/displayserver.c b/src/detection/displayserver/displayserver.c index 4a42a21a1..3bc47c345 100644 --- a/src/detection/displayserver/displayserver.c +++ b/src/detection/displayserver/displayserver.c @@ -19,15 +19,17 @@ uint32_t ffdsParseRefreshRate(int32_t refreshRate) return (uint32_t) refreshRate; } -bool ffdsAppendResolution(FFDisplayServerResult* result, uint32_t width, uint32_t height, uint32_t refreshRate) +bool ffdsAppendDisplay(FFDisplayServerResult* result, uint32_t width, uint32_t height, uint32_t refreshRate, uint32_t scaledWidth, uint32_t scaledHeight) { if(width == 0 || height == 0) return false; - FFResolutionResult* resolution = ffListAdd(&result->resolutions); - resolution->width = width; - resolution->height = height; - resolution->refreshRate = refreshRate; + FFDisplayResult* display = ffListAdd(&result->displays); + display->width = width; + display->height = height; + display->refreshRate = refreshRate; + display->scaledWidth = scaledWidth; + display->scaledHeight = scaledHeight; return true; } diff --git a/src/detection/displayserver/displayserver.h b/src/detection/displayserver/displayserver.h index d31d5edee..17fef3ba5 100644 --- a/src/detection/displayserver/displayserver.h +++ b/src/detection/displayserver/displayserver.h @@ -5,12 +5,14 @@ #include "fastfetch.h" -typedef struct FFResolutionResult +typedef struct FFDisplayResult { uint32_t width; uint32_t height; uint32_t refreshRate; -} FFResolutionResult; + uint32_t scaledWidth; + uint32_t scaledHeight; +} FFDisplayResult; typedef struct FFDisplayServerResult { @@ -20,13 +22,13 @@ typedef struct FFDisplayServerResult FFstrbuf deProcessName; FFstrbuf dePrettyName; FFstrbuf deVersion; - FFlist resolutions; //List of FFResolutionResult + FFlist displays; //List of FFDisplayResult } FFDisplayServerResult; const FFDisplayServerResult* ffConnectDisplayServer(const FFinstance* instance); //Used internal uint32_t ffdsParseRefreshRate(int32_t refreshRate); -bool ffdsAppendResolution(FFDisplayServerResult* result, uint32_t width, uint32_t height, uint32_t refreshRate); +bool ffdsAppendDisplay(FFDisplayServerResult* result, uint32_t width, uint32_t height, uint32_t refreshRate, uint32_t scaledWidth, uint32_t scaledHeight); #endif diff --git a/src/detection/displayserver/displayserver_apple.c b/src/detection/displayserver/displayserver_apple.c index 4d0d802c2..065e218b7 100644 --- a/src/detection/displayserver/displayserver_apple.c +++ b/src/detection/displayserver/displayserver_apple.c @@ -7,7 +7,7 @@ #include #include -static void detectResolution(FFDisplayServerResult* ds) +static void detectDisplays(FFDisplayServerResult* ds) { CGDirectDisplayID screens[128]; uint32_t screenCount; @@ -20,10 +20,12 @@ static void detectResolution(FFDisplayServerResult* ds) CGDisplayModeRef mode = CGDisplayCopyDisplayMode(screen); if(mode) { - ffdsAppendResolution(ds, + ffdsAppendDisplay(ds, + (uint32_t)CGDisplayModeGetPixelWidth(mode), + (uint32_t)CGDisplayModeGetPixelHeight(mode), + (uint32_t)CGDisplayModeGetRefreshRate(mode), (uint32_t)CGDisplayModeGetWidth(mode), - (uint32_t)CGDisplayModeGetHeight(mode), - (uint32_t)CGDisplayModeGetRefreshRate(mode) + (uint32_t)CGDisplayModeGetHeight(mode) ); CGDisplayModeRelease(mode); } @@ -84,6 +86,6 @@ void ffConnectDisplayServerImpl(FFDisplayServerResult* ds, const FFinstance* ins ffStrbufAppendS(&ds->deProcessName, "aqua"); ffStrbufAppendS(&ds->dePrettyName, "Aqua"); - ffListInitA(&ds->resolutions, sizeof(FFResolutionResult), 4); - detectResolution(ds); + ffListInitA(&ds->displays, sizeof(FFDisplayResult), 4); + detectDisplays(ds); } diff --git a/src/detection/displayserver/displayserver_nosupport.c b/src/detection/displayserver/displayserver_nosupport.c index 8427c7cb2..359ebc634 100644 --- a/src/detection/displayserver/displayserver_nosupport.c +++ b/src/detection/displayserver/displayserver_nosupport.c @@ -10,5 +10,5 @@ void ffConnectDisplayServerImpl(FFDisplayServerResult* ds, const FFinstance* ins ffStrbufInitA(&ds->deProcessName, 0); ffStrbufInitA(&ds->dePrettyName, 0); ffStrbufInitA(&ds->deVersion, 0); - ffListInitA(&ds->resolutions, sizeof(FFResolutionResult), 0); + ffListInitA(&ds->displays, sizeof(FFDisplayResult), 0); } diff --git a/src/detection/displayserver/displayserver_windows.c b/src/detection/displayserver/displayserver_windows.c index f39955b8b..ba5f4e8b4 100644 --- a/src/detection/displayserver/displayserver_windows.c +++ b/src/detection/displayserver/displayserver_windows.c @@ -4,6 +4,26 @@ #include #include +typedef struct +{ + WCHAR* deviceName; + uint32_t width; + uint32_t height; +} DataBundle; + +static CALLBACK WINBOOL enumMonitorProc(HMONITOR hMonitor, FF_MAYBE_UNUSED HDC hDC, FF_MAYBE_UNUSED LPRECT rc, LPARAM lparam) +{ + MONITORINFOEXW mi = { .cbSize = sizeof(mi) }; + DataBundle* data = (DataBundle*) lparam; + if(GetMonitorInfoW(hMonitor, (MONITORINFO *)&mi) && wcscmp(mi.szDevice, data->deviceName) == 0) + { + data->width = (uint32_t) mi.rcMonitor.right; + data->height = (uint32_t) mi.rcMonitor.bottom; + return FALSE; + } + return TRUE; +} + void ffConnectDisplayServerImpl(FFDisplayServerResult* ds, const FFinstance* instance) { FF_UNUSED(instance); @@ -23,7 +43,7 @@ void ffConnectDisplayServerImpl(FFDisplayServerResult* ds, const FFinstance* ins ffStrbufInit(&ds->deProcessName); ffStrbufInit(&ds->dePrettyName); ffStrbufInit(&ds->deVersion); - ffListInit(&ds->resolutions, sizeof(FFResolutionResult)); + ffListInit(&ds->displays, sizeof(FFDisplayResult)); DISPLAY_DEVICEW displayDevice = { .cb = sizeof(DISPLAY_DEVICEW) }; for(DWORD devNum = 0; EnumDisplayDevicesW(NULL, devNum, &displayDevice, 0) != 0; ++devNum) @@ -34,7 +54,13 @@ void ffConnectDisplayServerImpl(FFDisplayServerResult* ds, const FFinstance* ins if(EnumDisplaySettingsW(displayDevice.DeviceName, ENUM_CURRENT_SETTINGS, &devMode) == 0) continue; - ffdsAppendResolution(ds, devMode.dmPelsWidth, devMode.dmPelsHeight, devMode.dmDisplayFrequency); + DataBundle data = { + .deviceName = displayDevice.DeviceName, + .width = 0, + .height = 0, + }; + EnumDisplayMonitors(NULL, NULL, enumMonitorProc, (LPARAM)&data); + ffdsAppendDisplay(ds, devMode.dmPelsWidth, devMode.dmPelsHeight, devMode.dmDisplayFrequency, data.width, data.height); } //https://github.com/hykilpikonna/hyfetch/blob/master/neofetch#L2067 diff --git a/src/detection/displayserver/linux/displayserver_linux.c b/src/detection/displayserver/linux/displayserver_linux.c index 3f6fd04eb..257c60cc1 100644 --- a/src/detection/displayserver/linux/displayserver_linux.c +++ b/src/detection/displayserver/linux/displayserver_linux.c @@ -19,6 +19,9 @@ static void parseDRM(FFDisplayServerResult* result) struct dirent* entry; while((entry = readdir(dirp)) != NULL) { + if(strcmp(entry->d_name, ".") == 0 || strcmp(entry->d_name, "..") == 0) + continue; + ffStrbufAppendS(&drmDir, entry->d_name); ffStrbufAppendS(&drmDir, "/modes"); @@ -29,14 +32,16 @@ static void parseDRM(FFDisplayServerResult* result) continue; } - FFResolutionResult* resolution = ffListAdd(&result->resolutions); - resolution->width = 0; - resolution->height = 0; - resolution->refreshRate = 0; + FFDisplayResult* display = ffListAdd(&result->displays); + display->width = 0; + display->height = 0; + display->refreshRate = 0; + display->scaledWidth = 0; + display->scaledHeight = 0; - int scanned = fscanf(modeFile, "%ux%u", &resolution->width, &resolution->height); - if(scanned < 2 || resolution->width == 0 || resolution->height == 0) - --result->resolutions.length; + int scanned = fscanf(modeFile, "%ux%u", &display->width, &display->height); + if(scanned < 2 || display->width == 0 || display->height == 0) + --result->displays.length; fclose(modeFile); ffStrbufSubstrBefore(&drmDir, drmDirLength); @@ -54,31 +59,31 @@ void ffConnectDisplayServerImpl(FFDisplayServerResult* ds, const FFinstance* ins ffStrbufInit(&ds->deProcessName); ffStrbufInit(&ds->dePrettyName); ffStrbufInit(&ds->deVersion); - ffListInitA(&ds->resolutions, sizeof(FFResolutionResult), 4); + ffListInitA(&ds->displays, sizeof(FFDisplayResult), 4); //We try wayland as our prefered display server, as it supports the most features. //This method can't detect the name of our WM / DE ffdsConnectWayland(instance, ds); //Try the x11 libs, from most feature rich to least. - //We use the resolution list to detect if a connection is needed. - //They respect wmProtocolName, and only detect resolution if it is set. + //We use the display list to detect if a connection is needed. + //They respect wmProtocolName, and only detect display if it is set. - if(ds->resolutions.length == 0) + if(ds->displays.length == 0) ffdsConnectXcbRandr(instance, ds); - if(ds->resolutions.length == 0) + if(ds->displays.length == 0) ffdsConnectXrandr(instance, ds); - if(ds->resolutions.length == 0) + if(ds->displays.length == 0) ffdsConnectXcb(instance, ds); - if(ds->resolutions.length == 0) + if(ds->displays.length == 0) ffdsConnectXlib(instance, ds); - //This resolution detection method is display server independent. + //This display detection method is display server independent. //Use it if all connections failed - if(ds->resolutions.length == 0) + if(ds->displays.length == 0) parseDRM(ds); //This fills in missing information about WM / DE by using env vars and iterating processes diff --git a/src/detection/displayserver/linux/wayland.c b/src/detection/displayserver/linux/wayland.c index 37891149c..27a5c09d3 100644 --- a/src/detection/displayserver/linux/wayland.c +++ b/src/detection/displayserver/linux/wayland.c @@ -59,13 +59,15 @@ static void waylandOutputModeListener(void* data, struct wl_output* output, uint static FFThreadMutex mutex = FF_THREAD_MUTEX_INITIALIZER; ffThreadMutexLock(&mutex); - FFResolutionResult* result = ffListAdd(wldata->results); + FFDisplayResult* result = ffListAdd(wldata->results); ffThreadMutexUnlock(&mutex); result->width = (uint32_t) width; result->height = (uint32_t) height; result->refreshRate = ffdsParseRefreshRate(refreshRate / 1000); + result->scaledWidth = 0; + result->scaledHeight = 0; } static void waylandGlobalAddListener(void* data, struct wl_registry* registry, uint32_t name, const char* interface, uint32_t version) @@ -136,7 +138,7 @@ bool detectWayland(const FFinstance* instance, FFDisplayServerResult* result) return false; } - data.results = &result->resolutions; + data.results = &result->displays; struct wl_registry_listener registry_listener = { .global = waylandGlobalAddListener, @@ -151,7 +153,7 @@ bool detectWayland(const FFinstance* instance, FFDisplayServerResult* result) ffwl_display_disconnect(display); dlclose(wayland); - //We successfully connected to wayland and detected the resolution. + //We successfully connected to wayland and detected the display. //So we can set set the session type to wayland. //This is used as an indicator that we are running wayland by the x11 backends. ffStrbufSetS(&result->wmProtocolName, FF_DISPLAYSERVER_PROTOCOL_WAYLAND); diff --git a/src/detection/displayserver/linux/wmde.c b/src/detection/displayserver/linux/wmde.c index 0cb7c7493..4d9d79b6a 100644 --- a/src/detection/displayserver/linux/wmde.c +++ b/src/detection/displayserver/linux/wmde.c @@ -116,38 +116,34 @@ static void applyBetterWM(FFDisplayServerResult* result, const char* processName ffStrbufAppend(&result->wmPrettyName, &result->wmProcessName); } -static void getKDE(FFDisplayServerResult* result) +static void getKDE(const FFinstance* instance, FFDisplayServerResult* result) { ffStrbufSetS(&result->deProcessName, "plasmashell"); ffStrbufSetS(&result->dePrettyName, "KDE Plasma"); - ffParsePropFile(FASTFETCH_TARGET_DIR_USR"/share/xsessions/plasma.desktop", "X-KDE-PluginInfo-Version =", &result->deVersion); + ffParsePropFileData(instance, "xsessions/plasma.desktop", "X-KDE-PluginInfo-Version =", &result->deVersion); if(result->deVersion.length == 0) - ffParsePropFile(FASTFETCH_TARGET_DIR_USR"/share/xsessions/plasma5.desktop", "X-KDE-PluginInfo-Version =", &result->deVersion); + ffParsePropFileData(instance, "xsessions/plasma5.desktop", "X-KDE-PluginInfo-Version =", &result->deVersion); if(result->deVersion.length == 0) - ffParsePropFile(FASTFETCH_TARGET_DIR_USR"/share/wayland-sessions/plasmawayland.desktop", "X-KDE-PluginInfo-Version =", &result->deVersion); + ffParsePropFileData(instance, "wayland-sessions/plasmawayland.desktop", "X-KDE-PluginInfo-Version =", &result->deVersion); if(result->deVersion.length == 0) - ffParsePropFile(FASTFETCH_TARGET_DIR_USR"/share/wayland-sessions/plasmawayland5.desktop", "X-KDE-PluginInfo-Version =", &result->deVersion); - if(result->deVersion.length == 0) - ffParsePropFile(FASTFETCH_TARGET_DIR_USR"/local/share/xsessions/plasma.desktop", "X-KDE-PluginInfo-Version =", &result->deVersion); - if(result->deVersion.length == 0) - ffParsePropFile(FASTFETCH_TARGET_DIR_USR"/local/share/wayland-sessions/plasmawayland.desktop", "X-KDE-PluginInfo-Version =", &result->deVersion); + ffParsePropFileData(instance, "wayland-sessions/plasmawayland5.desktop", "X-KDE-PluginInfo-Version =", &result->deVersion); applyBetterWM(result, getenv("KDEWM")); } -static void getGnome(FFDisplayServerResult* result) +static void getGnome(const FFinstance* instance, FFDisplayServerResult* result) { ffStrbufSetS(&result->deProcessName, "gnome-shell"); ffStrbufSetS(&result->dePrettyName, "GNOME"); - ffParsePropFile(FASTFETCH_TARGET_DIR_USR"/share/gnome-shell/org.gnome.Extensions", "version :", &result->deVersion); + ffParsePropFileData(instance, "gnome-shell/org.gnome.Extensions", "version :", &result->deVersion); } -static void getCinnamon(FFDisplayServerResult* result) +static void getCinnamon(const FFinstance* instance, FFDisplayServerResult* result) { ffStrbufSetS(&result->deProcessName, "cinnamon"); ffStrbufSetS(&result->dePrettyName, "Cinnamon"); - ffParsePropFile(FASTFETCH_TARGET_DIR_USR"/share/applications/cinnamon.desktop", "X-GNOME-Bugzilla-Version =", &result->deVersion); + ffParsePropFileData(instance, "applications/cinnamon.desktop", "X-GNOME-Bugzilla-Version =", &result->deVersion); } static void getMate(const FFinstance* instance, FFDisplayServerResult* result) @@ -164,7 +160,7 @@ static void getMate(const FFinstance* instance, FFDisplayServerResult* result) FFstrbuf micro; ffStrbufInit(µ); - ffParsePropFileValues(FASTFETCH_TARGET_DIR_USR"/share/mate-about/mate-version.xml", 3, (FFpropquery[]) { + ffParsePropFileDataValues(instance, "mate-about/mate-version.xml", 3, (FFpropquery[]) { {"", &major}, {"", &minor}, {"", µ} @@ -193,7 +189,7 @@ static void getXFCE4(const FFinstance* instance, FFDisplayServerResult* result) { ffStrbufSetS(&result->deProcessName, "xfce4-session"); ffStrbufSetS(&result->dePrettyName, "Xfce4"); - ffParsePropFile(FASTFETCH_TARGET_DIR_USR"/share/gtk-doc/html/libxfce4ui/index.html", "

Version", &result->deVersion); + ffParsePropFileData(instance, "gtk-doc/html/libxfce4ui/index.html", "

Version", &result->deVersion); if(result->deVersion.length == 0 && instance->config.allowSlowOperations) { @@ -214,12 +210,12 @@ static void getLXQt(const FFinstance* instance, FFDisplayServerResult* result) { ffStrbufSetS(&result->deProcessName, "lxqt-session"); ffStrbufSetS(&result->dePrettyName, "LXQt"); - ffParsePropFile(FASTFETCH_TARGET_DIR_USR"/lib/pkgconfig/lxqt.pc", "Version:", &result->deVersion); + ffParsePropFileData(instance, "gconfig/lxqt.pc", "Version:", &result->deVersion); if(result->deVersion.length == 0) - ffParsePropFile(FASTFETCH_TARGET_DIR_USR"/share/cmake/lxqt/lxqt-config.cmake", "set ( LXQT_VERSION", &result->deVersion); + ffParsePropFileData(instance, "cmake/lxqt/lxqt-config.cmake", "set ( LXQT_VERSION", &result->deVersion); if(result->deVersion.length == 0) - ffParsePropFile(FASTFETCH_TARGET_DIR_USR"/share/cmake/lxqt/lxqt-config-version.cmake", "set ( PACKAGE_VERSION", &result->deVersion); + ffParsePropFileData(instance, "cmake/lxqt/lxqt-config-version.cmake", "set ( PACKAGE_VERSION", &result->deVersion); if(result->deVersion.length == 0 && instance->config.allowSlowOperations) { @@ -243,6 +239,13 @@ static void getLXQt(const FFinstance* instance, FFDisplayServerResult* result) ffStrbufDestroy(&wmProcessNameBuffer); } +static void getBudgie(const FFinstance* instance, FFDisplayServerResult* result) +{ + ffStrbufSetS(&result->dePrettyName, "Budgie"); + ffStrbufSetS(&result->deProcessName, "budgie-desktop"); + ffParsePropFileData(instance, "budgie/budgie-version.xml", "", &result->deVersion); +} + static void applyPrettyNameIfDE(const FFinstance* instance, FFDisplayServerResult* result, const char* name) { if(!ffStrSet(name)) @@ -253,19 +256,19 @@ static void applyPrettyNameIfDE(const FFinstance* instance, FFDisplayServerResul strcasecmp(name, "plasma") == 0 || strcasecmp(name, "plasmashell") == 0 || strcasecmp(name, "plasmawayland") == 0 - ) getKDE(result); + ) getKDE(instance, result); else if( strcasecmp(name, "Gnome") == 0 || strcasecmp(name, "ubuntu:GNOME") == 0 || strcasecmp(name, "ubuntu") == 0 || strcasecmp(name, "gnome-shell") == 0 - ) getGnome(result); + ) getGnome(instance, result); else if( strcasecmp(name, "X-Cinnamon") == 0 || strcasecmp(name, "Cinnamon") == 0 - ) getCinnamon(result); + ) getCinnamon(instance, result); else if( strcasecmp(name, "XFCE") == 0 || @@ -286,6 +289,13 @@ static void applyPrettyNameIfDE(const FFinstance* instance, FFDisplayServerResul strcasecmp(name, "X-LXQT") == 0 || strcasecmp(name, "lxqt-session") == 0 ) getLXQt(instance, result); + + else if( + strcasecmp(name, "Budgie") == 0 || + strcasecmp(name, "X-Budgie") == 0 || + strcasecmp(name, "budgie-desktop") == 0 || + strcasecmp(name, "Budgie:GNOME") == 0 + ) getBudgie(instance, result); } static void getWMProtocolNameFromEnv(FFDisplayServerResult* result) diff --git a/src/detection/displayserver/linux/xcb.c b/src/detection/displayserver/linux/xcb.c index 692be7808..99a960894 100644 --- a/src/detection/displayserver/linux/xcb.c +++ b/src/detection/displayserver/linux/xcb.c @@ -115,10 +115,12 @@ void ffdsConnectXcb(const FFinstance* instance, FFDisplayServerResult* result) while(iterator.rem > 0) { - ffdsAppendResolution( + ffdsAppendDisplay( result, (uint32_t) iterator.data->width_in_pixels, (uint32_t) iterator.data->height_in_pixels, + 0, + 0, 0 ); ffxcb_screen_next(&iterator); @@ -180,17 +182,19 @@ static bool xcbRandrHandleModeInfo(XcbRandrData* data, xcb_randr_mode_info_t* mo modeInfo->dot_clock / (uint32_t) (modeInfo->htotal * modeInfo->vtotal) )); - return ffdsAppendResolution( + return ffdsAppendDisplay( data->result, (uint32_t) modeInfo->width, (uint32_t) modeInfo->height, - refreshRate == 0 ? data->defaultRefreshRate : refreshRate + refreshRate == 0 ? data->defaultRefreshRate : refreshRate, + 0, + 0 ); } static bool xcbRandrHandleMode(XcbRandrData* data, xcb_randr_mode_t mode) { - //We do the check here, because we want the best fallback resolution if this call failed + //We do the check here, because we want the best fallback display if this call failed if(data->screenResources == NULL) return false; @@ -215,11 +219,13 @@ static bool xcbRandrHandleCrtc(XcbRandrData* data, xcb_randr_crtc_t crtc) return false; bool res = xcbRandrHandleMode(data, crtcInfoReply->mode); - res = res ? true : ffdsAppendResolution( + res = res ? true : ffdsAppendDisplay( data->result, (uint32_t) crtcInfoReply->width, (uint32_t) crtcInfoReply->height, - data->defaultRefreshRate + data->defaultRefreshRate, + 0, + 0 ); free(crtcInfoReply); @@ -258,11 +264,13 @@ static bool xcbRandrHandleMonitor(XcbRandrData* data, xcb_randr_monitor_info_t* data->ffxcb_randr_output_next(&outputIterator); }; - return foundOutput ? true : ffdsAppendResolution( + return foundOutput ? true : ffdsAppendDisplay( data->result, (uint32_t) monitor->width, (uint32_t) monitor->height, - data->defaultRefreshRate + data->defaultRefreshRate, + 0, + 0 ); } @@ -316,11 +324,13 @@ static void xcbRandrHandleScreen(XcbRandrData* data, xcb_screen_t* screen) return; //If detetction failed, fallback to screen = monitor, like in the libxcb.so implementation - ffdsAppendResolution( + ffdsAppendDisplay( data->result, (uint32_t) screen->width_in_pixels, (uint32_t) screen->height_in_pixels, - data->defaultRefreshRate + data->defaultRefreshRate, + 0, + 0 ); } diff --git a/src/detection/displayserver/linux/xlib.c b/src/detection/displayserver/linux/xlib.c index 108de5946..12f87587c 100644 --- a/src/detection/displayserver/linux/xlib.c +++ b/src/detection/displayserver/linux/xlib.c @@ -79,10 +79,12 @@ void ffdsConnectXlib(const FFinstance* instance, FFDisplayServerResult* result) for(int i = 0; i < ScreenCount(display); i++) { Screen* screen = ScreenOfDisplay(display, i); - ffdsAppendResolution( + ffdsAppendDisplay( result, (uint32_t) screen->width, (uint32_t) screen->height, + 0, + 0, 0 ); } @@ -138,11 +140,13 @@ static bool xrandrHandleModeInfo(XrandrData* data, XRRModeInfo* modeInfo) modeInfo->dotClock / (modeInfo->hTotal * modeInfo->vTotal) )); - return ffdsAppendResolution( + return ffdsAppendDisplay( data->result, (uint32_t) modeInfo->width, (uint32_t) modeInfo->height, - refreshRate == 0 ? data->defaultRefreshRate : refreshRate + refreshRate == 0 ? data->defaultRefreshRate : refreshRate, + 0, + 0 ); } @@ -158,7 +162,7 @@ static bool xrandrHandleMode(XrandrData* data, RRMode mode) static bool xrandrHandleCrtc(XrandrData* data, RRCrtc crtc) { - //We do the check here, because we want the best fallback resolution if this call failed + //We do the check here, because we want the best fallback display if this call failed if(data->screenResources == NULL) return false; @@ -167,11 +171,13 @@ static bool xrandrHandleCrtc(XrandrData* data, RRCrtc crtc) return false; bool res = xrandrHandleMode(data, crtcInfo->mode); - res = res ? true : ffdsAppendResolution( + res = res ? true : ffdsAppendDisplay( data->result, (uint32_t) crtcInfo->width, (uint32_t) crtcInfo->height, - data->defaultRefreshRate + data->defaultRefreshRate, + 0, + 0 ); data->ffXRRFreeCrtcInfo(crtcInfo); @@ -201,11 +207,13 @@ static bool xrandrHandleMonitor(XrandrData* data, XRRMonitorInfo* monitorInfo) foundOutput = true; } - return foundOutput ? true : ffdsAppendResolution( + return foundOutput ? true : ffdsAppendDisplay( data->result, (uint32_t) monitorInfo->width, (uint32_t) monitorInfo->height, - data->defaultRefreshRate + data->defaultRefreshRate, + 0, + 0 ); } @@ -253,11 +261,13 @@ static void xrandrHandleScreen(XrandrData* data, Screen* screen) return; //Fallback to screen - ffdsAppendResolution( + ffdsAppendDisplay( data->result, (uint32_t) WidthOfScreen(screen), (uint32_t) HeightOfScreen(screen), - data->defaultRefreshRate + data->defaultRefreshRate, + 0, + 0 ); } diff --git a/src/detection/gpu/gpu.h b/src/detection/gpu/gpu.h index 16bd2ff53..e14feb9d8 100644 --- a/src/detection/gpu/gpu.h +++ b/src/detection/gpu/gpu.h @@ -8,12 +8,21 @@ #define FF_GPU_TEMP_UNSET (0/0.0) #define FF_GPU_CORE_COUNT_UNSET -1 +#define FF_GPU_VENDOR_NAME_APPLE "Apple" #define FF_GPU_VENDOR_NAME_AMD "AMD" #define FF_GPU_VENDOR_NAME_INTEL "Intel" #define FF_GPU_VENDOR_NAME_NVIDIA "NVIDIA" +typedef enum FFGpuType +{ + FF_GPU_TYPE_INTEGRATED, + FF_GPU_TYPE_DISCRETE, + FF_GPU_TYPE_UNKNOWN +} FFGpuType; + typedef struct FFGPUResult { + FFGpuType type; FFstrbuf vendor; FFstrbuf name; FFstrbuf driver; diff --git a/src/detection/gpu/gpu_apple.c b/src/detection/gpu/gpu_apple.c index 52292d83e..413e65746 100644 --- a/src/detection/gpu/gpu_apple.c +++ b/src/detection/gpu/gpu_apple.c @@ -5,6 +5,7 @@ #include "util/apple/cf_helpers.h" #include +#include static double detectGpuTemp(const FFstrbuf* gpuName) { @@ -59,7 +60,33 @@ const char* ffDetectGPUImpl(FFlist* gpus, const FFinstance* instance) FFGPUResult* gpu = ffListAdd(gpus); - ffStrbufInitA(&gpu->vendor, 0); + gpu->type = FF_GPU_TYPE_UNKNOWN; + + ffStrbufInit(&gpu->vendor); + int vendorId; + if(!ffCfDictGetInt(properties, CFSTR("vendor-id"), &vendorId)) + { + if(vendorId == 0x106b) + { + ffStrbufAppendS(&gpu->vendor, FF_GPU_VENDOR_NAME_APPLE); + gpu->type = FF_GPU_TYPE_INTEGRATED; + } + else if(wmemchr((const wchar_t[]) {0x1002, 0x1022}, (wchar_t)vendorId, 2)) + { + ffStrbufAppendS(&gpu->vendor, FF_GPU_VENDOR_NAME_AMD); + gpu->type = FF_GPU_TYPE_DISCRETE; // Apple never used AMD CPUs + } + else if(wmemchr((const wchar_t[]) {0x03e7, 0x8086, 0x8087}, (wchar_t)vendorId, 3)) + { + ffStrbufAppendS(&gpu->vendor, FF_GPU_VENDOR_NAME_INTEL); + gpu->type = FF_GPU_TYPE_INTEGRATED; + } + else if(wmemchr((const wchar_t[]) {0x0955, 0x10de, 0x12d2}, (wchar_t)vendorId, 3)) + { + ffStrbufAppendS(&gpu->vendor, FF_GPU_VENDOR_NAME_NVIDIA); + gpu->type = FF_GPU_TYPE_DISCRETE; + } + } ffStrbufInit(&gpu->driver); ffCfDictGetString(properties, CFSTR("CFBundleIdentifier"), &gpu->driver); diff --git a/src/detection/gpu/gpu_linux.c b/src/detection/gpu/gpu_linux.c index 50580a898..d34807df9 100644 --- a/src/detection/gpu/gpu_linux.c +++ b/src/detection/gpu/gpu_linux.c @@ -58,10 +58,10 @@ static void pciDetectVendorName(FFGPUResult* gpu, PCIData* pci, struct pci_dev* ffStrbufSetS(&gpu->vendor, FF_GPU_VENDOR_NAME_NVIDIA); } -static void drmDetectDeviceName(FFGPUResult* gpu, PCIData* pci, struct pci_dev* device) +static void drmDetectDeviceName(const FFinstance* instance, FFGPUResult* gpu, PCIData* pci, struct pci_dev* device) { u8 revId; - bool revIdSet = false;; + bool revIdSet = false; #if PCI_LIB_VERSION >= 0x030800 revIdSet = pci->ffpci_fill_info(device, PCI_FILL_CLASS_EXT) & PCI_FILL_CLASS_EXT; @@ -82,7 +82,7 @@ static void drmDetectDeviceName(FFGPUResult* gpu, PCIData* pci, struct pci_dev* ffStrbufInit(&query); ffStrbufAppendF(&query, "%X, %X,", device->device_id, revId); - ffParsePropFile(FASTFETCH_TARGET_DIR_USR"/share/libdrm/amdgpu.ids", query.chars, &gpu->name); + ffParsePropFileData(instance, "libdrm/amdgpu.ids", query.chars, &gpu->name); ffStrbufDestroy(&query); @@ -94,11 +94,11 @@ static void drmDetectDeviceName(FFGPUResult* gpu, PCIData* pci, struct pci_dev* ffStrbufRemoveStringsA(&gpu->name, sizeof(removeStrings) / sizeof(removeStrings[0]), removeStrings); } -static void pciDetectDeviceName(FFGPUResult* gpu, PCIData* pci, struct pci_dev* device) +static void pciDetectDeviceName(const FFinstance* instance, FFGPUResult* gpu, PCIData* pci, struct pci_dev* device) { if(ffStrbufCompS(&gpu->vendor, FF_GPU_VENDOR_NAME_AMD) == 0) { - drmDetectDeviceName(gpu, pci, device); + drmDetectDeviceName(instance, gpu, pci, device); if(gpu->name.length > 0) return; } @@ -146,20 +146,16 @@ static void pciDetectDriverName(FFGPUResult* gpu, PCIData* pci, struct pci_dev* ffStrbufDestroy(&path); } -static void pciDetectTemperatur(const FFinstance* instance, FFGPUResult* gpu, struct pci_dev* device) +static void pciDetectTemperatur(FFGPUResult* gpu, struct pci_dev* device) { - const FFTempsResult* tempsResult = ffDetectTemps(instance); + const FFTempsResult* tempsResult = ffDetectTemps(); for(uint32_t i = 0; i < tempsResult->values.length; i++) { FFTempValue* tempValue = ffListGet(&tempsResult->values, i); - uint32_t tempClass; - if(sscanf(tempValue->deviceClass.chars, "%x", &tempClass) != 1) - continue; - //The kernel exposes the device class multiplied by 256 for some reason - if(tempClass == device->device_class * 256) + if(tempValue->deviceClass == device->device_class * 256) { gpu->temperature = tempValue->value; return; @@ -167,6 +163,38 @@ static void pciDetectTemperatur(const FFinstance* instance, FFGPUResult* gpu, st } } +static void detectType(FFGPUResult* gpu, const PCIData* pci, struct pci_dev* device) +{ + //There is no straightforward way to detect the type of a GPU. + //The approach taken here is to look at the memory sizes of the device. + //Since integrated GPUs usually use the system ram, they don't have expansive ROMs + //and their memory sizes are usually smaller than 1GB. + + if(!(pci->ffpci_fill_info(device, PCI_FILL_SIZES) & PCI_FILL_SIZES)) + { + gpu->type = FF_GPU_TYPE_UNKNOWN; + return; + } + + if(device->rom_size > 0) + { + gpu->type = FF_GPU_TYPE_DISCRETE; + return; + } + + uint32_t numSizes = sizeof(device->size) / sizeof(device->size[0]); + for(uint32_t i = 0; i < numSizes; i++) + { + if(device->size[i] >= 1024 * 1024 * 1024) //1GB + { + gpu->type = FF_GPU_TYPE_DISCRETE; + return; + } + } + + gpu->type = FF_GPU_TYPE_INTEGRATED; +} + static void pciHandleDevice(const FFinstance* instance, FFlist* results, PCIData* pci, struct pci_dev* device) { pci->ffpci_fill_info(device, PCI_FILL_CLASS); @@ -175,7 +203,9 @@ static void pciHandleDevice(const FFinstance* instance, FFlist* results, PCIData pci->ffpci_lookup_name(pci->access, class, sizeof(class) - 1, PCI_LOOKUP_CLASS, device->device_class); if( + //https://pci-ids.ucw.cz/read/PD/03 strcasecmp("VGA compatible controller", class) != 0 && + strcasecmp("XGA compatible controller", class) != 0 && strcasecmp("3D controller", class) != 0 && strcasecmp("Display controller", class) != 0 ) return; @@ -188,16 +218,18 @@ static void pciHandleDevice(const FFinstance* instance, FFlist* results, PCIData pciDetectVendorName(gpu, pci, device); ffStrbufInit(&gpu->name); - pciDetectDeviceName(gpu, pci, device); + pciDetectDeviceName(instance, gpu, pci, device); ffStrbufInit(&gpu->driver); pciDetectDriverName(gpu, pci, device); + detectType(gpu, pci, device); + gpu->coreCount = FF_GPU_CORE_COUNT_UNSET; gpu->temperature = FF_GPU_TEMP_UNSET; if(instance->config.gpuTemp) - pciDetectTemperatur(instance, gpu, device); + pciDetectTemperatur(gpu, device); } static const char* pciDetectGPUs(const FFinstance* instance, FFlist* gpus) diff --git a/src/detection/gpu/gpu_windows.cpp b/src/detection/gpu/gpu_windows.cpp index f5d0f4946..07b711205 100644 --- a/src/detection/gpu/gpu_windows.cpp +++ b/src/detection/gpu/gpu_windows.cpp @@ -40,6 +40,8 @@ static const char* detectWithDxgi(FFlist* gpus) ffStrbufInit(&gpu->driver); + gpu->type = desc.DedicatedVideoMemory >= 1024 * 1024 * 1024 ? FF_GPU_TYPE_DISCRETE : FF_GPU_TYPE_INTEGRATED; + adapter->Release(); gpu->temperature = FF_GPU_TEMP_UNSET; @@ -65,6 +67,8 @@ static const char* detectWithWmi(FFlist* gpus) { FFGPUResult* gpu = (FFGPUResult*)ffListAdd(gpus); + gpu->type = FF_GPU_TYPE_UNKNOWN; + ffStrbufInit(&gpu->vendor); record.getString(L"AdapterCompatibility", &gpu->vendor); if(ffStrbufStartsWithS(&gpu->vendor, "Intel ")) diff --git a/src/detection/gtk.c b/src/detection/gtk.c index ad24c9c4e..1db344edd 100644 --- a/src/detection/gtk.c +++ b/src/detection/gtk.c @@ -80,8 +80,11 @@ static void detectGTKFromSettings(const FFinstance* instance, FFGTKResult* resul cursorTheme = ffSettingsGet(instance, "/org/mate/peripherals-mouse/cursor-theme", "org.mate.peripherals-mouse", NULL, "cursor-theme", FF_VARIANT_TYPE_STRING).strValue; cursorSize = ffSettingsGet(instance, "/org/mate/peripherals-mouse/cursor-size", "org.mate.peripherals-mouse", NULL, "cursor-size", FF_VARIANT_TYPE_INT).intValue; } - else if(ffStrbufIgnCaseCompS(&wmde->dePrettyName, "Gnome") == 0 || ffStrbufIgnCaseCompS(&wmde->dePrettyName, "Unity") == 0 || ffStrbufIgnCaseCompS(&wmde->dePrettyName, "Budgie") == 0) - { + else if( + ffStrbufIgnCaseCompS(&wmde->dePrettyName, "Gnome") == 0 || + ffStrbufIgnCaseCompS(&wmde->dePrettyName, "Unity") == 0 || + ffStrbufIgnCaseCompS(&wmde->dePrettyName, "Budgie") == 0 + ) { themeName = ffSettingsGet(instance, "/org/gnome/desktop/interface/gtk-theme", "org.gnome.desktop.interface", NULL, "gtk-theme", FF_VARIANT_TYPE_STRING).strValue; iconsName = ffSettingsGet(instance, "/org/gnome/desktop/interface/icon-theme", "org.gnome.desktop.interface", NULL, "icon-theme", FF_VARIANT_TYPE_STRING).strValue; fontName = ffSettingsGet(instance, "/org/gnome/desktop/interface/font-name", "org.gnome.desktop.interface", NULL, "font-name", FF_VARIANT_TYPE_STRING).strValue; @@ -145,7 +148,7 @@ static void detectGTKFromConfigDir(FFstrbuf* configDir, const char* version, FFG static void detectGTK(const FFinstance* instance, const char* version, FFGTKResult* result) { - //Mate, Cinnamon and Gnome use dconf to save theme config + //Mate, Cinnamon, Gnome, Unity, Budgie use dconf to save theme config //On other DEs, this will do nothing detectGTKFromSettings(instance, result); if(allPropertiesSet(result)) @@ -155,9 +158,9 @@ static void detectGTK(const FFinstance* instance, const char* version, FFGTKResu FFstrbuf baseDir; ffStrbufInitA(&baseDir, 64); - for(uint32_t i = 0; i < instance->state.configDirs.length; i++) + FF_LIST_FOR_EACH(FFstrbuf, configDir, instance->state.platform.configDirs) { - ffStrbufSet(&baseDir, ffListGet(&instance->state.configDirs, i)); + ffStrbufSet(&baseDir, configDir); detectGTKFromConfigDir(&baseDir, version, result); if(allPropertiesSet(result)) break; diff --git a/src/detection/os/os.h b/src/detection/os/os.h index 688d6f63b..80fb6b2af 100644 --- a/src/detection/os/os.h +++ b/src/detection/os/os.h @@ -17,8 +17,6 @@ typedef struct FFOSResult FFstrbuf versionID; FFstrbuf codename; FFstrbuf buildID; - FFstrbuf systemName; - FFstrbuf architecture; } FFOSResult; const FFOSResult* ffDetectOS(const FFinstance* instance); diff --git a/src/detection/os/os_android.c b/src/detection/os/os_android.c index eb4bf97d5..890486ab7 100644 --- a/src/detection/os/os_android.c +++ b/src/detection/os/os_android.c @@ -23,10 +23,6 @@ void ffDetectOSImpl(FFOSResult* os, const FFinstance* instance) ffStrbufInit(&os->buildID); ffSettingsGetAndroidProperty("ro.build.id", &os->buildID); - ffStrbufInitS(&os->systemName, instance->state.utsname.sysname); - - ffStrbufInitS(&os->architecture, instance->state.utsname.machine); - ffStrbufInit(&os->idLike); ffStrbufInit(&os->variant); ffStrbufInit(&os->variantID); diff --git a/src/detection/os/os_apple.m b/src/detection/os/os_apple.m index d6a410506..8f8174387 100644 --- a/src/detection/os/os_apple.m +++ b/src/detection/os/os_apple.m @@ -61,8 +61,6 @@ void ffDetectOSImpl(FFOSResult* os, const FFinstance* instance) ffStrbufInit(&os->id); ffStrbufInit(&os->prettyName); ffStrbufInit(&os->versionID); - ffStrbufInit(&os->systemName); - ffStrbufInit(&os->architecture); ffStrbufInitA(&os->codename, 0); ffStrbufInitA(&os->idLike, 0); @@ -82,8 +80,6 @@ void ffDetectOSImpl(FFOSResult* os, const FFinstance* instance) ffStrbufAppend(&os->prettyName, &os->name); ffStrbufAppend(&os->versionID, &os->version); - ffSysctlGetString("kern.ostype", &os->systemName); - ffSysctlGetString("hw.machine", &os->architecture); parseOSXSoftwareLicense(os); } diff --git a/src/detection/os/os_linux.c b/src/detection/os/os_linux.c index 73c513922..45a0f5cec 100644 --- a/src/detection/os/os_linux.c +++ b/src/detection/os/os_linux.c @@ -138,11 +138,6 @@ void ffDetectOSImpl(FFOSResult* os, const FFinstance* instance) ffStrbufInit(&os->versionID); ffStrbufInit(&os->codename); ffStrbufInit(&os->buildID); - ffStrbufInit(&os->systemName); - ffStrbufInit(&os->architecture); - - ffStrbufSetS(&os->systemName, instance->state.utsname.sysname); - ffStrbufSetS(&os->architecture, instance->state.utsname.machine); detectOS(os, instance); diff --git a/src/detection/os/os_windows.cpp b/src/detection/os/os_windows.cpp index fdda72c89..7b4c6ea2f 100644 --- a/src/detection/os/os_windows.cpp +++ b/src/detection/os/os_windows.cpp @@ -46,6 +46,8 @@ static const char* getOsNameByWinbrand(FFstrbuf* osName) extern "C" void ffDetectOSImpl(FFOSResult* os, const FFinstance* instance) { + FF_UNUSED(instance); + ffStrbufInit(&os->name); ffStrbufInit(&os->prettyName); ffStrbufInit(&os->id); @@ -56,8 +58,6 @@ void ffDetectOSImpl(FFOSResult* os, const FFinstance* instance) ffStrbufInit(&os->versionID); ffStrbufInit(&os->codename); ffStrbufInit(&os->buildID); - ffStrbufInit(&os->systemName); - ffStrbufInit(&os->architecture); if(getOsNameByWinbrand(&os->variant) && getOsNameByWmi(&os->variant)) return; @@ -106,7 +106,4 @@ void ffDetectOSImpl(FFOSResult* os, const FFinstance* instance) } 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_linux.c b/src/detection/packages/packages_linux.c index f4b363d54..ace7d637e 100644 --- a/src/detection/packages/packages_linux.c +++ b/src/detection/packages/packages_linux.c @@ -140,7 +140,7 @@ static uint32_t getNixPackagesImpl(char* path) 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"); + 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 | wc -l"); ffProcessAppendStdOut(&output, (char* const[]) { "sh", @@ -149,13 +149,12 @@ static uint32_t getNixPackagesImpl(char* path) 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++; + int result = (int) strtol(output.chars, NULL, 10); + ffStrbufDestroy(&command); ffStrbufDestroy(&output); - return result; + + return (uint32_t) result; } static uint32_t getNixPackages(FFstrbuf* baseDir, const char* dirname) @@ -272,7 +271,7 @@ static void getPackageCounts(const FFinstance* instance, FFstrbuf* baseDir, FFPa 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->rpm += getSQLite3Int(instance, baseDir, "/var/lib/rpm/rpmdb.sqlite", "SELECT count(blob) FROM Packages"); packageCounts->snap += getSnap(baseDir); packageCounts->xbps += getXBPS(baseDir, "/var/db/xbps"); } @@ -309,6 +308,8 @@ static void getPackageCountsBedrock(const FFinstance* instance, FFstrbuf* baseDi { if(entry->d_type != DT_DIR) continue; + if(strcmp(entry->d_name, ".") == 0 || strcmp(entry->d_name, "..") == 0) + continue; ffStrbufAppendS(baseDir, entry->d_name); getPackageCounts(instance, baseDir, packageCounts); @@ -338,7 +339,7 @@ void ffDetectPackagesImpl(const FFinstance* instance, FFPackagesResult* result) result->rpm = getRpmFromLibrpm(instance); #endif - ffStrbufSetS(&baseDir, instance->state.passwd->pw_dir); + ffStrbufSet(&baseDir, &instance->state.platform.homeDir); result->nixUser = getNixPackages(&baseDir, "/.nix-profile"); ffStrbufDestroy(&baseDir); diff --git a/src/detection/packages/packages_windows.c b/src/detection/packages/packages_windows.c index 93739e9bf..afd016c6d 100644 --- a/src/detection/packages/packages_windows.c +++ b/src/detection/packages/packages_windows.c @@ -17,10 +17,10 @@ static uint32_t getNumElements(const char* searchPath /* including `\*` suffix * counter++; } while (FindNextFileA(hFind, &wfd)); FindClose(hFind); - } - if(type == FILE_ATTRIBUTE_DIRECTORY && counter >= 2) - counter -= 2; // accounting for . and .. + if(type == FILE_ATTRIBUTE_DIRECTORY && counter >= 2) + counter -= 2; // accounting for . and .. + } return counter; } @@ -28,17 +28,15 @@ static uint32_t getNumElements(const char* searchPath /* including `\*` suffix * static void detectScoop(const FFinstance* instance, FFPackagesResult* result) { char scoopPath[MAX_PATH + 3]; - strcpy(scoopPath, instance->state.passwd->pw_dir); + strcpy(scoopPath, instance->state.platform.homeDir.chars); 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) +static void detectChoco(FF_MAYBE_UNUSED const FFinstance* instance, FFPackagesResult* result) { - FF_UNUSED(instance); - const char* chocoInstall = getenv("ChocolateyInstall"); if(!chocoInstall || chocoInstall[0] == '\0') return; @@ -51,8 +49,22 @@ static void detectChoco(const FFinstance* instance, FFPackagesResult* result) result->choco--; // choco } +static void detectPacman(FF_MAYBE_UNUSED const FFinstance* instance, FFPackagesResult* result) +{ + const char* msystemPrefix = getenv("MSYSTEM_PREFIX"); + if(!msystemPrefix) + return; + + // MSYS2 + char pacmanPath[MAX_PATH + 3]; + strcpy(pacmanPath, msystemPrefix); + strncat(pacmanPath, "/../var/lib/pacman/local/*", sizeof(pacmanPath) - 1 - strlen(pacmanPath)); + result->pacman = getNumElements(pacmanPath, FILE_ATTRIBUTE_DIRECTORY); +} + void ffDetectPackagesImpl(const FFinstance* instance, FFPackagesResult* result) { detectScoop(instance, result); detectChoco(instance, result); + detectPacman(instance, result); } diff --git a/src/detection/qt.c b/src/detection/qt.c index 6a7794c8e..5ac1d943e 100644 --- a/src/detection/qt.c +++ b/src/detection/qt.c @@ -88,9 +88,9 @@ static void detectPlasma(const FFinstance* instance, FFQtResult* result) FFstrbuf baseDir; ffStrbufInitA(&baseDir, 64); - for(uint32_t i = 0; i < instance->state.configDirs.length; i++) + FF_LIST_FOR_EACH(FFstrbuf, configDir, instance->state.platform.configDirs) { - ffStrbufSet(&baseDir, ffListGet(&instance->state.configDirs, i)); + ffStrbufSet(&baseDir, configDir); ffStrbufAppendS(&baseDir, "kdeglobals"); if(detectPlasmaFromFile(baseDir.chars, result)) diff --git a/src/detection/temps/temps_linux.c b/src/detection/temps/temps_linux.c index cb342a9f8..913e6f037 100644 --- a/src/detection/temps/temps_linux.c +++ b/src/detection/temps/temps_linux.c @@ -8,18 +8,19 @@ static bool parseHwmonDir(FFstrbuf* dir, FFTempValue* value) { + //https://www.kernel.org/doc/Documentation/hwmon/sysfs-interface uint32_t dirLength = dir->length; - FFstrbuf valueBuffer; + FF_STRBUF_AUTO_DESTROY valueBuffer; ffStrbufInit(&valueBuffer); ffStrbufAppendS(dir, "temp1_input"); - ffReadFileBuffer(dir->chars, &valueBuffer); + if(!ffReadFileBuffer(dir->chars, &valueBuffer)) + return false; + ffStrbufSubstrBefore(dir, dirLength); - value->value = ffStrbufToDouble(&valueBuffer); - - ffStrbufDestroy(&valueBuffer); + value->value = ffStrbufToDouble(&valueBuffer) / 1000; // valueBuffer is millidegree Celsius if(value->value != value->value) return false; @@ -29,13 +30,19 @@ static bool parseHwmonDir(FFstrbuf* dir, FFTempValue* value) ffStrbufSubstrBefore(dir, dirLength); ffStrbufAppendS(dir, "device/class"); - ffReadFileBuffer(dir->chars, &value->deviceClass); - ffStrbufSubstrBefore(dir, dirLength); + if(!ffReadFileBuffer(dir->chars, &valueBuffer)) + { + ffStrbufSubstrBefore(dir, dirLength); + ffStrbufAppendS(dir, "device/device/class"); + ffReadFileBuffer(dir->chars, &valueBuffer); + } + if(valueBuffer.length) + value->deviceClass = (uint32_t) strtoul(valueBuffer.chars, NULL, 16); - return value->name.length > 0 || value->deviceClass.length > 0; + return value->name.length > 0 || value->deviceClass > 0; } -const FFTempsResult* ffDetectTemps(const FFinstance* instance) +const FFTempsResult* ffDetectTemps() { static FFTempsResult result; static FFThreadMutex mutex = FF_THREAD_MUTEX_INITIALIZER; @@ -49,13 +56,6 @@ const FFTempsResult* ffDetectTemps(const FFinstance* instance) } init = true; - if(!instance->config.allowSlowOperations) - { - ffListInitA(&result.values, sizeof(FFTempValue), 0); - ffThreadMutexUnlock(&mutex); - return &result; - } - ffListInitA(&result.values, sizeof(FFTempValue), 16); FFstrbuf baseDir; @@ -83,11 +83,10 @@ const FFTempsResult* ffDetectTemps(const FFinstance* instance) FFTempValue* temp = ffListAdd(&result.values); ffStrbufInit(&temp->name); - ffStrbufInit(&temp->deviceClass); + temp->deviceClass = 0; if(!parseHwmonDir(&baseDir, temp)) { ffStrbufDestroy(&temp->name); - ffStrbufDestroy(&temp->deviceClass); --result.values.length; } diff --git a/src/detection/temps/temps_linux.h b/src/detection/temps/temps_linux.h index 6bece23ed..569bd0401 100644 --- a/src/detection/temps/temps_linux.h +++ b/src/detection/temps/temps_linux.h @@ -8,7 +8,7 @@ typedef struct FFTempValue { FFstrbuf name; - FFstrbuf deviceClass; + uint32_t deviceClass; double value; } FFTempValue; @@ -17,6 +17,6 @@ typedef struct FFTempsResult FFlist values; //List of FFTempValue } FFTempsResult; -const FFTempsResult* ffDetectTemps(const FFinstance* instance); +const FFTempsResult* ffDetectTemps(); #endif diff --git a/src/detection/terminalfont/terminalfont.c b/src/detection/terminalfont/terminalfont.c index 5716b720d..41ff5428b 100644 --- a/src/detection/terminalfont/terminalfont.c +++ b/src/detection/terminalfont/terminalfont.c @@ -37,7 +37,7 @@ static void detectAlacritty(const FFinstance* instance, FFTerminalFontResult* te ffStrbufDestroy(&fontSize); } -static void detectTTY(FFTerminalFontResult* terminalFont) +FF_MAYBE_UNUSED static void detectTTY(FFTerminalFontResult* terminalFont) { FFstrbuf fontName; ffStrbufInit(&fontName); @@ -291,7 +291,7 @@ static void detectFromWindowsTeriminal(const FFinstance* instance, const FFstrbu #endif #endif //defined(_WIN32) || defined(__linux__) -static bool detectKitty(const FFinstance* instance, FFTerminalFontResult* result) +FF_MAYBE_UNUSED static bool detectKitty(const FFinstance* instance, FFTerminalFontResult* result) { FFstrbuf fontName; ffStrbufInit(&fontName); @@ -320,21 +320,54 @@ static bool detectKitty(const FFinstance* instance, FFTerminalFontResult* result return true; } +static bool detectWezterm(FF_MAYBE_UNUSED const FFinstance* instance, FFTerminalFontResult* result) +{ + FF_STRBUF_AUTO_DESTROY fontName; + ffStrbufInit(&fontName); + + ffStrbufSetS(&result->error, ffProcessAppendStdOut(&fontName, (char* const[]){ + "wezterm", + "ls-fonts", + "--text", + "a", + NULL + })); + if(result->error.length) + return false; + + //LeftToRight + // 0 a \u{61} x_adv=7 cells=1 glyph=a,180 wezterm.font("JetBrains Mono", {weight="Regular", stretch="Normal", style="Normal"}) + // , BuiltIn + ffStrbufSubstrAfterFirstC(&fontName, '"'); + ffStrbufSubstrBeforeFirstC(&fontName, '"'); + + if(!fontName.length) + return false; + + ffFontInitCopy(&result->font, fontName.chars); + 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) + if(ffStrbufStartsWithIgnCaseS(&terminalShell->terminalProcessName, "alacritty")) detectAlacritty(instance, terminalFont); - else if(ffStrbufIgnCaseCompS(&terminalShell->terminalProcessName, "kitty") == 0) + else if(ffStrbufStartsWithIgnCaseS(&terminalShell->terminalProcessName, "wezterm-gui")) + detectWezterm(instance, terminalFont); + + #ifndef _WIN32 + else if(ffStrbufIgnCaseEqualS(&terminalShell->terminalProcessName, "kitty")) detectKitty(instance, terminalFont); else if(ffStrbufStartsWithIgnCaseS(&terminalShell->terminalExe, "/dev/tty")) detectTTY(terminalFont); + #endif #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) + else if(ffStrbufIgnCaseEqualS(&terminalShell->terminalProcessName, "Windows Terminal") || + ffStrbufIgnCaseEqualS(&terminalShell->terminalProcessName, "WindowsTerminal.exe")) detectFromWindowsTeriminal(instance, &terminalShell->terminalExe, terminalFont); #endif diff --git a/src/detection/terminalfont/terminalfont_apple.m b/src/detection/terminalfont/terminalfont_apple.m index 1ee387aa2..71ecc8b22 100644 --- a/src/detection/terminalfont/terminalfont_apple.m +++ b/src/detection/terminalfont/terminalfont_apple.m @@ -17,7 +17,7 @@ static void detectIterm2(const FFinstance* instance, FFTerminalFontResult* termi } NSError* error; - NSString* fileName = [NSString stringWithFormat:@"file://%s/Library/Preferences/com.googlecode.iterm2.plist", instance->state.passwd->pw_dir]; + NSString* fileName = [NSString stringWithFormat:@"file://%s/Library/Preferences/com.googlecode.iterm2.plist", instance->state.platform.homeDir.chars]; NSDictionary* dict = [NSDictionary dictionaryWithContentsOfURL:[NSURL URLWithString:fileName] error:&error]; if(error) @@ -62,7 +62,7 @@ static void detectAppleTerminal(FFTerminalFontResult* terminalFont) 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]; + NSString* fileName = [NSString stringWithFormat:@"file://%s/Library/Preferences/dev.warp.Warp-Stable.plist", instance->state.platform.homeDir.chars]; NSDictionary* dict = [NSDictionary dictionaryWithContentsOfURL:[NSURL URLWithString:fileName] error:&error]; if(error) diff --git a/src/detection/terminalfont/terminalfont_linux.c b/src/detection/terminalfont/terminalfont_linux.c index 54ea3a328..1d06d09c1 100644 --- a/src/detection/terminalfont/terminalfont_linux.c +++ b/src/detection/terminalfont/terminalfont_linux.c @@ -88,15 +88,15 @@ static void detectKonsole(const FFinstance* instance, FFTerminalFontResult* term } FFstrbuf profilePath; - ffStrbufInitA(&profilePath, 64); - ffStrbufAppendS(&profilePath, ".local/share/konsole/"); + ffStrbufInitA(&profilePath, 32); + ffStrbufAppendS(&profilePath, "konsole/"); ffStrbufAppend(&profilePath, &profile); ffStrbufDestroy(&profile); FFstrbuf fontName; ffStrbufInit(&fontName); - ffParsePropFileHome(instance, profilePath.chars, "Font =", &fontName); + ffParsePropFileData(instance, profilePath.chars, "Font =", &fontName); if(fontName.length == 0) ffStrbufAppendF(&terminalFont->error, "Couldn't find \"Font=%%[^\\n]\" in \"%s\"", profilePath.chars); @@ -149,7 +149,9 @@ static void detectDeepinTerminal(const FFinstance* instance, FFTerminalFontResul ffStrbufInit(&fontSize); FFstrbuf profile; - ffStrbufInitF(&profile, "%s/.config/deepin/deepin-terminal/config.conf", instance->state.passwd->pw_dir); + ffStrbufInit(&profile); + ffStrbufAppend(&profile, &instance->state.platform.homeDir); + ffStrbufAppendS(&profile, ".config/deepin/deepin-terminal/config.conf"); //TODO: Use config dirs FILE* file = fopen(profile.chars, "r"); if(file) diff --git a/src/detection/terminalfont/terminalfont_windows.c b/src/detection/terminalfont/terminalfont_windows.c index ea41f018e..060108290 100644 --- a/src/detection/terminalfont/terminalfont_windows.c +++ b/src/detection/terminalfont/terminalfont_windows.c @@ -14,10 +14,14 @@ static void detectMintty(const FFinstance* instance, FFTerminalFontResult* termi FF_STRBUF_AUTO_DESTROY fontSize; ffStrbufInit(&fontSize); - ffParsePropFileHomeValues(instance, ".minttyrc", 2, (FFpropquery[]) { + if(!ffParsePropFileConfigValues(instance, "mintty/config", 2, (FFpropquery[]) { {"Font=", &fontName}, {"FontHeight=", &fontSize} - }); + })) + ffParsePropFileConfigValues(instance, ".minttyrc", 2, (FFpropquery[]) { + {"Font=", &fontName}, + {"FontHeight=", &fontSize} + }); if(fontName.length == 0) ffStrbufAppendS(&fontName, "Lucida Console"); if(fontSize.length == 0) diff --git a/src/detection/terminalshell/terminalshell.c b/src/detection/terminalshell/terminalshell.c index a99c498e4..aa9d221f3 100644 --- a/src/detection/terminalshell/terminalshell.c +++ b/src/detection/terminalshell/terminalshell.c @@ -1,5 +1,7 @@ #include "fastfetch.h" +#include "common/io.h" #include "common/processing.h" +#include "common/properties.h" #ifdef _WIN32 @@ -36,122 +38,252 @@ static bool getFileVersion(const char* exePath, FFstrbuf* version) #endif -static void getShellVersionBash(FFstrbuf* exe, FFstrbuf* version) +static bool getExeVersionRaw(FFstrbuf* exe, FFstrbuf* version) { - ffProcessAppendStdOut(version, (char* const[]) { + return ffProcessAppendStdOut(version, (char* const[]) { exe->chars, "--version", NULL - }); // GNU bash, version 5.1.16(1)-release (x86_64-pc-msys)\nCopyright... + }) == NULL; +} + +static bool getExeVersionGeneral(FFstrbuf* exe, FFstrbuf* version) +{ + if(!getExeVersionRaw(exe, version)) + return false; + + ffStrbufSubstrAfterFirstC(version, ' '); + ffStrbufSubstrBeforeFirstC(version, ' '); + return true; +} + +static bool getShellVersionBash(FFstrbuf* exe, FFstrbuf* version) +{ + if(!getExeVersionRaw(exe, version)) + return false; + + // 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 + return true; } -static void getShellVersionZsh(FFstrbuf* exe, FFstrbuf* version) +static bool getShellVersionFish(FFstrbuf* exe, FFstrbuf* version) { - ffProcessAppendStdOut(version, (char* const[]) { - exe->chars, - "--version", - NULL - }); - ffStrbufSubstrBeforeLastC(version, ' '); - ffStrbufSubstrAfterFirstC(version, ' '); -} + if(!getExeVersionRaw(exe, version)) + return false; -static void getShellVersionFish(FFstrbuf* exe, FFstrbuf* version) -{ - ffProcessAppendStdOut(version, (char* const[]) { - exe->chars, - "--version", - NULL - }); + //fish, version 3.6.0 ffStrbufTrimRight(version, '\n'); ffStrbufSubstrAfterLastC(version, ' '); + return true; } -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) +static bool getShellVersionPwsh(FFstrbuf* exe, FFstrbuf* version) { #ifdef _WIN32 if(getFileVersion(exe->chars, version)) - return; + { + ffStrbufSubstrBeforeLastC(version, '.'); + return true; + } #endif - ffProcessAppendStdOut(version, (char* const[]) { - exe->chars, - "--version", - NULL - }); + if(!getExeVersionRaw(exe, version)) + return false; + ffStrbufTrimRight(version, '\n'); ffStrbufSubstrAfterLastC(version, ' '); + return true; } #ifdef _WIN32 -static void getShellVersionWinPowerShell(FFstrbuf* exe, FFstrbuf* version) +static bool getShellVersionWinPowerShell(FFstrbuf* exe, FFstrbuf* version) { - ffProcessAppendStdOut(version, (char* const[]) { + if(ffProcessAppendStdOut(version, (char* const[]) { exe->chars, "-NoLogo", "-NoProfile", "-Command", "$PSVersionTable.PSVersion.ToString()", NULL - }); + })) return false; + ffStrbufTrimRight(version, '\n'); ffStrbufSubstrAfterLastC(version, ' '); + return true; } -static void getShellVersionCmd(FFstrbuf* exe, FFstrbuf* version) +static bool getShellVersionCmd(FFstrbuf* exe, FFstrbuf* version) { - getFileVersion(exe->chars, version); + return 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); + return getShellVersionBash(exe, version); + if(strcasecmp(exeName, "zsh") == 0) + return getExeVersionGeneral(exe, version); //zsh 5.9 (arm-apple-darwin21.3.0) + if(strcasecmp(exeName, "fish") == 0) + return getShellVersionFish(exe, version); + if(strcasecmp(exeName, "pwsh") == 0) + return getShellVersionPwsh(exe, version); + if(strcasecmp(exeName, "csh") == 0 || strcasecmp(exeName, "tcsh") == 0) + return getExeVersionGeneral(exe, version); //tcsh 6.24.07 (Astron) 2022-12-21 (aarch64-apple-darwin) options wide,nls,dl,al,kan,sm,rh,color,filec + if(strcasecmp(exeName, "nu") == 0) + return getExeVersionRaw(exe, version); //0.73.0 #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); + if(strcasecmp(exeName, "powershell") == 0 || strcasecmp(exeName, "powershell_ise") == 0) + return getShellVersionWinPowerShell(exe, version); + if(strcasecmp(exeName, "cmd") == 0) + return getShellVersionCmd(exe, version); #endif - else - ok = false; - return ok; + return false; +} + +FF_MAYBE_UNUSED static bool getTerminalVersionTermux(FFstrbuf* version) +{ + ffStrbufSetS(version, getenv("TERMUX_VERSION")); + return version->length > 0; +} + +FF_MAYBE_UNUSED static bool getTerminalVersionGnome(FFstrbuf* version) +{ + if(ffProcessAppendStdOut(version, (char* const[]){ + "gnome-terminal", + "--version", + NULL + })) return false; + + //# GNOME Terminal 3.46.7 using VTE 0.70.2 +BIDI +GNUTLS +ICU +SYSTEMD + ffStrbufSubstrAfterFirstS(version, "Terminal "); + ffStrbufSubstrBeforeFirstC(version, ' '); + return true; +} + +FF_MAYBE_UNUSED static bool getTerminalVersionKonsole(FFstrbuf* exe, FFstrbuf* version) +{ + const char* konsoleVersion = getenv("KONSOLE_VERSION"); + if(konsoleVersion) + { + //221201 + long major = strtol(konsoleVersion, NULL, 10); + if (major >= 0) + { + long patch = major % 100; + major /= 100; + long minor = major % 100; + major /= 100; + ffStrbufSetF(version, "%ld.%ld.%ld", major, minor, patch); + return true; + } + } + + return getExeVersionGeneral(exe, version); +} + +#ifdef _WIN32 + +static bool getTerminalVersionWindowsTerminal(FFstrbuf* exe, FFstrbuf* version) +{ + FF_STRBUF_AUTO_DESTROY buildInfoPath; + ffStrbufInitNS(&buildInfoPath, ffStrbufLastIndexC(exe, '\\') + 1, exe->chars); + ffStrbufAppendS(&buildInfoPath, "BuildInfo.xml"); + + if(ffParsePropFile(buildInfoPath.chars, "StoreVersion=\"", version)) + { + ffStrbufTrimRight(version, '"'); + return true; + } + + return getFileVersion(exe->chars, version); +} + +static bool getTerminalVersionConEmu(FFstrbuf* exe, FFstrbuf* version) +{ + ffStrbufSetS(version, getenv("ConEmuBuild")); + + if(version->length) + return true; + + return getFileVersion(exe->chars, version); +} + +#endif + +bool fftsGetTerminalVersion(FFstrbuf* processName, FF_MAYBE_UNUSED FFstrbuf* exe, FFstrbuf* version) +{ + #ifdef __ANDROID__ + + if(ffStrbufEqualS(processName, "Termux")) + return getTerminalVersionTermux(version); + + #endif + + #if defined(__linux__) || defined(__FreeBSD__) + + if(ffStrbufIgnCaseEqualS(processName, "gnome-terminal-")) + return getTerminalVersionGnome(version); + + if(ffStrbufIgnCaseEqualS(processName, "konsole")) + return getTerminalVersionKonsole(exe, version); + + if(ffStrbufIgnCaseEqualS(processName, "xfce4-terminal")) + return getExeVersionGeneral(exe, version);//xfce4-terminal 1.0.4 (Xfce 4.18)... + + if(ffStrbufIgnCaseEqualS(processName, "deepin-terminal")) + return getExeVersionGeneral(exe, version);//deepin-terminal 5.4.36 + + #endif + + #ifdef _WIN32 + + if(ffStrbufIgnCaseEqualS(processName, "WindowsTerminal.exe")) + return getTerminalVersionWindowsTerminal(exe, version); + + if(ffStrbufStartsWithIgnCaseS(processName, "ConEmuC")) + return getTerminalVersionConEmu(exe, version); + + #endif + + #ifndef _WIN32 + + if(ffStrbufIgnCaseEqualS(processName, "kitty")) + return getExeVersionGeneral(exe, version); //kitty 0.21.2 created by Kovid Goyal + + #endif + + if(ffStrbufStartsWithIgnCaseS(processName, "alacritty")) + return getExeVersionGeneral(exe, version); + + const char* termProgramVersion = getenv("TERM_PROGRAM_VERSION"); + if(termProgramVersion) + { + const char* termProgram = getenv("TERM_PROGRAM"); + if(termProgram) + { + if(ffStrbufStartsWithIgnCaseS(processName, termProgram) || // processName ends with `.exe` on Windows + (strcmp(termProgram, "vscode") == 0 && ffStrbufStartsWithIgnCaseS(processName, "code")) + ) { + ffStrbufSetS(version, termProgramVersion); + return true; + } + } + } + + #ifdef _WIN32 + + return getFileVersion(exe->chars, version); + + #else + + return false; + + #endif } diff --git a/src/detection/terminalshell/terminalshell.h b/src/detection/terminalshell/terminalshell.h index 8783e3410..a2a1f4200 100644 --- a/src/detection/terminalshell/terminalshell.h +++ b/src/detection/terminalshell/terminalshell.h @@ -17,6 +17,7 @@ typedef struct FFTerminalShellResult FFstrbuf terminalExe; FFstrbuf terminalPrettyName; const char* terminalExeName; //pointer to a char in terminalExe + FFstrbuf terminalVersion; FFstrbuf userShellExe; const char* userShellExeName; //pointer to a char in userShellExe diff --git a/src/detection/terminalshell/terminalshell_linux.c b/src/detection/terminalshell/terminalshell_linux.c index 80c0d93d1..1f30866c2 100644 --- a/src/detection/terminalshell/terminalshell_linux.c +++ b/src/detection/terminalshell/terminalshell_linux.c @@ -261,10 +261,7 @@ static void getTerminalFromEnv(FFTerminalShellResult* result) 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")); + ffStrbufSet(&result->userShellExe, &instance->state.platform.userShell); if(result->userShellExe.length == 0) return; @@ -310,6 +307,8 @@ static void getShellVersion(FFstrbuf* exe, const char* exeName, FFstrbuf* versio getShellVersionGeneric(exe, exeName, version); } +bool fftsGetTerminalVersion(FFstrbuf* processName, FFstrbuf* exe, FFstrbuf* version); + const FFTerminalShellResult* ffDetectTerminalShell(const FFinstance* instance) { FF_UNUSED(instance); @@ -350,12 +349,17 @@ const FFTerminalShellResult* ffDetectTerminalShell(const FFinstance* instance) 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); + ffStrbufClear(&result.shellVersion); + if(instance->config.shellVersion) + { + 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"); @@ -373,11 +377,18 @@ const FFTerminalShellResult* ffDetectTerminalShell(const FFinstance* instance) ffStrbufInitS(&result.terminalPrettyName, "Apple Terminal"); else if(ffStrbufEqualS(&result.terminalProcessName, "WarpTerminal")) ffStrbufInitS(&result.terminalPrettyName, "Warp"); + else if(ffStrbufEqualS(&result.terminalProcessName, "wezterm-gui")) + ffStrbufInitS(&result.terminalPrettyName, "WezTerm"); 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); + ffStrbufInit(&result.terminalVersion); + + if(instance->config.terminalVersion) + fftsGetTerminalVersion(&result.terminalProcessName, &result.terminalExe, &result.terminalVersion); + ffThreadMutexUnlock(&mutex); return &result; } diff --git a/src/detection/terminalshell/terminalshell_windows.cpp b/src/detection/terminalshell/terminalshell_windows.cpp index cb2da1ca4..b800228fa 100644 --- a/src/detection/terminalshell/terminalshell_windows.cpp +++ b/src/detection/terminalshell/terminalshell_windows.cpp @@ -99,7 +99,7 @@ static bool getProcessInfo(uint32_t pid, uint32_t* ppid, FFstrbuf* pname, FFstrb extern "C" bool fftsGetShellVersion(FFstrbuf* exe, const char* exeName, FFstrbuf* version); -static uint32_t getShellInfo(FFTerminalShellResult* result, uint32_t pid) +static uint32_t getShellInfo(const FFinstance* instance, FFTerminalShellResult* result, uint32_t pid) { uint32_t ppid; @@ -128,11 +128,12 @@ static uint32_t getShellInfo(FFTerminalShellResult* result, uint32_t pid) ffStrbufClear(&result->shellPrettyName); ffStrbufClear(&result->shellExe); result->shellExeName = nullptr; - return getShellInfo(result, ppid); + return getShellInfo(instance, result, ppid); } ffStrbufClear(&result->shellVersion); - fftsGetShellVersion(&result->shellExe, result->shellPrettyName.chars, &result->shellVersion); + if(instance->config.shellVersion) + fftsGetShellVersion(&result->shellExe, result->shellPrettyName.chars, &result->shellVersion); if(ffStrbufIgnCaseEqualS(&result->shellPrettyName, "pwsh")) ffStrbufSetS(&result->shellPrettyName, "PowerShell"); @@ -175,7 +176,7 @@ static uint32_t getShellInfo(FFTerminalShellResult* result, uint32_t pid) return ppid; } -static uint32_t getTerminalInfo(FFTerminalShellResult* result, uint32_t pid) +static uint32_t getTerminalInfo(const FFinstance* instance, FFTerminalShellResult* result, uint32_t pid) { uint32_t ppid; @@ -201,7 +202,7 @@ static uint32_t getTerminalInfo(FFTerminalShellResult* result, uint32_t pid) ffStrbufClear(&result->terminalPrettyName); ffStrbufClear(&result->terminalExe); result->terminalExeName = ""; - return getTerminalInfo(result, ppid); + return getTerminalInfo(instance, result, ppid); } if(ffStrbufIgnCaseEqualS(&result->terminalPrettyName, "WindowsTerminal")) @@ -217,6 +218,8 @@ static uint32_t getTerminalInfo(FFTerminalShellResult* result, uint32_t pid) ffStrbufSetS(&result->terminalPrettyName, "Windows Explorer"); else if(ffStrbufStartsWithIgnCaseS(&result->terminalPrettyName, "ConEmuC")) ffStrbufSetS(&result->terminalPrettyName, "ConEmu"); + else if(ffStrbufEqualS(&result->terminalPrettyName, "wezterm-gui")) + ffStrbufInitS(&result->terminalPrettyName, "WezTerm"); return ppid; } @@ -263,6 +266,8 @@ static void getTerminalFromEnv(FFTerminalShellResult* result) } } +extern "C" bool fftsGetTerminalVersion(FFstrbuf* processName, FFstrbuf* exe, FFstrbuf* version); + const FFTerminalShellResult* ffDetectTerminalShell(const FFinstance* instance) { FF_UNUSED(instance); @@ -297,11 +302,15 @@ const FFTerminalShellResult* ffDetectTerminalShell(const FFinstance* instance) if(!getProcessInfo(0, &ppid, nullptr, nullptr, nullptr)) goto exit; - ppid = getShellInfo(&result, ppid); - getTerminalInfo(&result, ppid); + ppid = getShellInfo(instance, &result, ppid); + getTerminalInfo(instance, &result, ppid); if(result.terminalProcessName.length == 0) getTerminalFromEnv(&result); + ffStrbufInit(&result.terminalVersion); + if(instance->config.terminalVersion) + fftsGetTerminalVersion(&result.terminalProcessName, &result.terminalExe, &result.terminalVersion); + exit: ffThreadMutexUnlock(&mutex); return &result; diff --git a/src/detection/title.c b/src/detection/title.c deleted file mode 100644 index df9367ba0..000000000 --- a/src/detection/title.c +++ /dev/null @@ -1,66 +0,0 @@ -#include "fastfetch.h" -#include "detection/title.h" -#include "common/thread.h" - -#include -#include - -#ifndef HOST_NAME_MAX - #define HOST_NAME_MAX 64 -#endif - -#ifdef __linux__ -#include - -static void detectFQDN(FFTitleResult* title) -{ - struct addrinfo hints = {0}; - hints.ai_socktype = SOCK_STREAM; - hints.ai_flags = AI_CANONNAME; - - struct addrinfo* info = NULL; - - if(getaddrinfo(title->hostname.chars, "80", &hints, &info) == 0) - { - struct addrinfo* current = info; - while(title->fqdn.length == 0 && current != NULL) - { - ffStrbufAppendS(&title->fqdn, current->ai_canonname); - current = current->ai_next; - } - - freeaddrinfo(info); - } -} -#endif - -const FFTitleResult* ffDetectTitle(const FFinstance* instance) -{ - static FFTitleResult result; - - static FFThreadMutex mutex = FF_THREAD_MUTEX_INITIALIZER; - static bool init = false; - ffThreadMutexLock(&mutex); - if(init) - { - ffThreadMutexUnlock(&mutex); - return &result; - } - init = true; - - ffStrbufInit(&result.userName); - ffStrbufAppendS(&result.userName, instance->state.passwd->pw_name); - - ffStrbufInitA(&result.hostname, HOST_NAME_MAX); - ffStrbufAppendS(&result.hostname, instance->state.utsname.nodename); - - ffStrbufInitA(&result.fqdn, HOST_NAME_MAX); - #ifdef __linux__ - detectFQDN(&result); - #endif - if(result.fqdn.length == 0) - ffStrbufAppend(&result.fqdn, &result.hostname); - - ffThreadMutexUnlock(&mutex); - return &result; -} diff --git a/src/detection/title.h b/src/detection/title.h deleted file mode 100644 index 9dc31d312..000000000 --- a/src/detection/title.h +++ /dev/null @@ -1,17 +0,0 @@ -#pragma once - -#ifndef FF_INCLUDED_detection_title -#define FF_INCLUDED_detection_title - -#include "fastfetch.h" - -typedef struct FFTitleResult -{ - FFstrbuf userName; - FFstrbuf hostname; - FFstrbuf fqdn; //Fully qualified domain name -} FFTitleResult; - -const FFTitleResult* ffDetectTitle(const FFinstance* instance); - -#endif diff --git a/src/detection/users/users_windows.c b/src/detection/users/users_windows.c index 7ed1c70fa..5c78cfdd7 100644 --- a/src/detection/users/users_windows.c +++ b/src/detection/users/users_windows.c @@ -2,7 +2,6 @@ #include "util/windows/unicode.h" #include -#include "wtsapi32_extend.h" void ffDetectUsers(FFlist* users, FFstrbuf* error) { diff --git a/src/detection/users/wtsapi32_extend.h b/src/detection/users/wtsapi32_extend.h deleted file mode 100644 index 5c89beb2f..000000000 --- a/src/detection/users/wtsapi32_extend.h +++ /dev/null @@ -1,45 +0,0 @@ -#pragma once - -//at the time of writing, of MinGW doesn't have the definition of WTSEnumerateSessionsExW and friends - -#if (_WIN32_WINNT >= 0x0601) - -typedef struct _WTS_SESSION_INFO_1A { - DWORD ExecEnvId; - WTS_CONNECTSTATE_CLASS State; - DWORD SessionId; - LPSTR pSessionName; - LPSTR pHostName; - LPSTR pUserName; - LPSTR pDomainName; - LPSTR pFarmName; -} WTS_SESSION_INFO_1A, *PWTS_SESSION_INFO_1A; - -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; - -#define WTS_SESSION_INFO_1 __MINGW_NAME_AW(WTS_SESSION_INFO_1) -#define PWTS_SESSION_INFO_1 __MINGW_NAME_AW(PWTS_SESSION_INFO_1) - -WINBOOL WINAPI WTSEnumerateSessionsExA(HANDLE hServer,DWORD* pLevel,DWORD Filter,PWTS_SESSION_INFO_1A* ppSessionInfo,DWORD* pCount); -WINBOOL WINAPI WTSEnumerateSessionsExW(HANDLE hServer,DWORD* pLevel,DWORD Filter,PWTS_SESSION_INFO_1W* ppSessionInfo,DWORD* pCount); -#define WTSEnumerateSessionsEx __MINGW_NAME_AW(WTSEnumerateSessionsEx) - -typedef enum _WTS_TYPE_CLASS { - WTSTypeProcessInfoLevel0, - WTSTypeProcessInfoLevel1, - WTSTypeSessionInfoLevel1 -} WTS_TYPE_CLASS; -BOOL WTSFreeMemoryExA(WTS_TYPE_CLASS WTSTypeClass,PVOID pMemory,ULONG NumberOfEntries); -BOOL WTSFreeMemoryExW(WTS_TYPE_CLASS WTSTypeClass,PVOID pMemory,ULONG NumberOfEntries); -#define WTSFreeMemoryEx __MINGW_NAME_AW(WTSFreeMemoryEx) - -#endif /*(_WIN32_WINNT >= 0x0601)*/ diff --git a/src/detection/vulkan.c b/src/detection/vulkan.c index 4119280a7..dbda127bb 100644 --- a/src/detection/vulkan.c +++ b/src/detection/vulkan.c @@ -190,6 +190,11 @@ static const char* detectVulkan(const FFinstance* instance, FFVulkanResult* resu ffStrbufInit(&gpu->name); ffStrbufAppendS(&gpu->name, physicalDeviceProperties.properties.deviceName); + if(physicalDeviceProperties.properties.deviceType == VK_PHYSICAL_DEVICE_TYPE_INTEGRATED_GPU) + gpu->type = FF_GPU_TYPE_INTEGRATED; + else + gpu->type = FF_GPU_TYPE_DISCRETE; + //No way to detect those using vulkan ffStrbufInit(&gpu->vendor); ffStrbufInit(&gpu->driver); diff --git a/src/detection/wifi/wifi.h b/src/detection/wifi/wifi.h index 375299a76..fae5c46ce 100644 --- a/src/detection/wifi/wifi.h +++ b/src/detection/wifi/wifi.h @@ -16,24 +16,17 @@ struct FFWifiConnection FFstrbuf status; FFstrbuf ssid; FFstrbuf macAddress; - FFstrbuf phyType; + FFstrbuf protocol; + FFstrbuf security; 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*/); diff --git a/src/detection/wifi/wifi_android.c b/src/detection/wifi/wifi_android.c new file mode 100644 index 000000000..af1012fe7 --- /dev/null +++ b/src/detection/wifi/wifi_android.c @@ -0,0 +1,62 @@ +#include "wifi.h" + +#include "common/processing.h" +#include "common/properties.h" + +const char* ffDetectWifi(FF_MAYBE_UNUSED const FFinstance* instance, FFlist* result) +{ + FF_STRBUF_AUTO_DESTROY buffer; + ffStrbufInit(&buffer); + + if(ffProcessAppendStdOut(&buffer, (char* const[]){ + FASTFETCH_TARGET_DIR_ROOT "/libexec/termux-api", + "WifiConnectionInfo" + })) + return "Starting `" FASTFETCH_TARGET_DIR_ROOT "/libexec/termux-api" " WifiConnectionInfo` failed"; + + 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.protocol); + ffStrbufInit(&item->conn.security); + item->conn.signalQuality = 0.0/0.0; + item->conn.rxRate = 0.0/0.0; + item->conn.txRate = 0.0/0.0; + + if(!ffParsePropLines(buffer.chars, "\"supplicant_state\": ", &item->inf.status)) + ffStrbufAppendS(&item->inf.status, "Unknown"); + + { + ffStrbufTrimRight(&item->inf.status, ','); + ffStrbufTrim(&item->inf.status, '"'); + if(!ffStrbufEqualS(&item->inf.status, "COMPLETED")) + return NULL; + } + + if(ffParsePropLines(buffer.chars, "\"rssi\": ", &item->inf.description)) + { + double rssi = ffStrbufToDouble(&item->inf.description); + item->conn.signalQuality = rssi >= -50 ? 100 : rssi <= -100 ? 0 : (rssi + 100) * 2; + ffStrbufClear(&item->inf.description); + } + + if(ffParsePropLines(buffer.chars, "\"network_id\": ", &item->inf.description)) + ffStrbufTrimRight(&item->inf.description, ','); + + if(ffParsePropLines(buffer.chars, "\"bssid\": ", &item->conn.macAddress)) + { + ffStrbufTrimRight(&item->conn.macAddress, ','); + ffStrbufTrim(&item->conn.macAddress, '"'); + } + + if(ffParsePropLines(buffer.chars, "\"ssid\": ", &item->conn.ssid)) + { + ffStrbufTrimRight(&item->conn.ssid, ','); + ffStrbufTrim(&item->conn.ssid, '"'); + } + + return NULL; +} diff --git a/src/detection/wifi/wifi_apple.m b/src/detection/wifi/wifi_apple.m index 4f09ae1a7..a83f79485 100644 --- a/src/detection/wifi/wifi_apple.m +++ b/src/detection/wifi/wifi_apple.m @@ -10,9 +10,6 @@ const char* ffDetectWifi(const FFinstance* instance, FFlist* result) 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); @@ -21,13 +18,11 @@ const char* ffDetectWifi(const FFinstance* instance, FFlist* result) ffStrbufInit(&item->conn.status); ffStrbufInit(&item->conn.ssid); ffStrbufInit(&item->conn.macAddress); - ffStrbufInit(&item->conn.phyType); + ffStrbufInit(&item->conn.protocol); + ffStrbufInit(&item->conn.security); 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"); @@ -43,88 +38,88 @@ const char* ffDetectWifi(const FFinstance* instance, FFlist* result) switch(inf.activePHYMode) { case kCWPHYModeNone: - ffStrbufAppendS(&item->conn.phyType, "none"); + ffStrbufAppendS(&item->conn.protocol, "none"); break; case kCWPHYMode11a: - ffStrbufAppendS(&item->conn.phyType, "802.11a"); + ffStrbufAppendS(&item->conn.protocol, "802.11a"); break; case kCWPHYMode11b: - ffStrbufAppendS(&item->conn.phyType, "802.11b"); + ffStrbufAppendS(&item->conn.protocol, "802.11b"); break; case kCWPHYMode11g: - ffStrbufAppendS(&item->conn.phyType, "802.11g"); + ffStrbufAppendS(&item->conn.protocol, "802.11g"); break; case kCWPHYMode11n: - ffStrbufAppendS(&item->conn.phyType, "802.11n (Wi-Fi 4)"); + ffStrbufAppendS(&item->conn.protocol, "802.11n (Wi-Fi 4)"); break; case kCWPHYMode11ac: - ffStrbufAppendS(&item->conn.phyType, "802.11ac (Wi-Fi 5)"); + ffStrbufAppendS(&item->conn.protocol, "802.11ac (Wi-Fi 5)"); break; case 6 /*kCWPHYMode11ax*/: - ffStrbufAppendS(&item->conn.phyType, "802.11ax (Wi-Fi 6)"); + ffStrbufAppendS(&item->conn.protocol, "802.11ax (Wi-Fi 6)"); break; case 7 /*kCWPHYMode11be?*/: - ffStrbufAppendS(&item->conn.phyType, "802.11be (Wi-Fi 7)"); + ffStrbufAppendS(&item->conn.protocol, "802.11be (Wi-Fi 7)"); break; default: - ffStrbufAppendF(&item->conn.phyType, "Unknown (%ld)", inf.activePHYMode); + ffStrbufAppendF(&item->conn.protocol, "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"); + ffStrbufAppendS(&item->conn.security, "Insecure"); break; case kCWSecurityWEP: - ffStrbufAppendS(&item->security.algorithm, "WEP"); + ffStrbufAppendS(&item->conn.security, "WEP"); break; case kCWSecurityWPAPersonal: - ffStrbufAppendS(&item->security.algorithm, "WPA Personal"); + ffStrbufAppendS(&item->conn.security, "WPA Personal"); break; case kCWSecurityWPAPersonalMixed: - ffStrbufAppendS(&item->security.algorithm, "WPA Persional Mixed"); + ffStrbufAppendS(&item->conn.security, "WPA Persional Mixed"); break; case kCWSecurityWPA2Personal: - ffStrbufAppendS(&item->security.algorithm, "WPA2 Personal"); + ffStrbufAppendS(&item->conn.security, "WPA2 Personal"); break; case kCWSecurityPersonal: - ffStrbufAppendS(&item->security.algorithm, "Personal"); + ffStrbufAppendS(&item->conn.security, "Personal"); break; case kCWSecurityDynamicWEP: - ffStrbufAppendS(&item->security.algorithm, "Dynamic WEP"); + ffStrbufAppendS(&item->conn.security, "Dynamic WEP"); break; case kCWSecurityWPAEnterprise: - ffStrbufAppendS(&item->security.algorithm, "WPA Enterprise"); + ffStrbufAppendS(&item->conn.security, "WPA Enterprise"); break; case kCWSecurityWPAEnterpriseMixed: - ffStrbufAppendS(&item->security.algorithm, "WPA Enterprise Mixed"); + ffStrbufAppendS(&item->conn.security, "WPA Enterprise Mixed"); break; case kCWSecurityWPA2Enterprise: - ffStrbufAppendS(&item->security.algorithm, "WPA2 Enterprise"); + ffStrbufAppendS(&item->conn.security, "WPA2 Enterprise"); break; case kCWSecurityEnterprise: - ffStrbufAppendS(&item->security.algorithm, "Enterprise"); + ffStrbufAppendS(&item->conn.security, "Enterprise"); break; case kCWSecurityWPA3Personal: - ffStrbufAppendS(&item->security.algorithm, "WPA3 Personal"); + ffStrbufAppendS(&item->conn.security, "WPA3 Personal"); break; case kCWSecurityWPA3Enterprise: - ffStrbufAppendS(&item->security.algorithm, "WPA3 Enterprise"); + ffStrbufAppendS(&item->conn.security, "WPA3 Enterprise"); break; case kCWSecurityWPA3Transition: - ffStrbufAppendS(&item->security.algorithm, "WPA3 Transition"); + ffStrbufAppendS(&item->conn.security, "WPA3 Transition"); break; case 14 /*kCWSecurityOWE*/: - ffStrbufAppendS(&item->security.algorithm, "OWE"); + ffStrbufAppendS(&item->conn.security, "OWE"); break; case 15 /*kCWSecurityOWETransition*/: - ffStrbufAppendS(&item->security.algorithm, "OWE Transition"); + ffStrbufAppendS(&item->conn.security, "OWE Transition"); break; default: - ffStrbufAppendF(&item->security.algorithm, "Unknown (%ld)", inf.security); + ffStrbufAppendF(&item->conn.security, "Unknown (%ld)", inf.security); break; } } diff --git a/src/detection/wifi/wifi_linux.c b/src/detection/wifi/wifi_linux.c index 985ba6f74..a8f70620d 100644 --- a/src/detection/wifi/wifi_linux.c +++ b/src/detection/wifi/wifi_linux.c @@ -1,20 +1,214 @@ #include "wifi.h" -#include "common/io.h" -#include -#include +#include +#include -static const char* detectInf(uint32_t ifIndex, FFWifiResult* wifi) +#ifdef FF_HAVE_LIBNM + +#include "common/processing.h" +#include "common/properties.h" +#include "common/library.h" + +#include +#define NM_NO_INCLUDE_EXTRA_HEADERS 1 +#include + +static const char* detectWifiWithLibnm(const FFinstance* instance, FFlist* result) { - //TODO: play with netlink - FF_UNUSED(ifIndex, wifi); - return "Unimplemented"; + FF_LIBRARY_LOAD(nm, &instance->config.libnm, "dlopen libnm failed", "libnm" FF_LIBRARY_EXTENSION, 0); + FF_LIBRARY_LOAD_SYMBOL_MESSAGE(nm, nm_client_new); + FF_LIBRARY_LOAD_SYMBOL_MESSAGE(nm, nm_client_get_devices); + FF_LIBRARY_LOAD_SYMBOL_MESSAGE(nm, nm_device_get_iface); + FF_LIBRARY_LOAD_SYMBOL_MESSAGE(nm, nm_device_get_state); + FF_LIBRARY_LOAD_SYMBOL_MESSAGE(nm, nm_device_wifi_get_type); + FF_LIBRARY_LOAD_SYMBOL_MESSAGE(nm, nm_device_wifi_get_active_access_point); + FF_LIBRARY_LOAD_SYMBOL_MESSAGE(nm, nm_access_point_get_ssid); + FF_LIBRARY_LOAD_SYMBOL_MESSAGE(nm, nm_utils_ssid_to_utf8); + FF_LIBRARY_LOAD_SYMBOL_MESSAGE(nm, nm_access_point_get_bssid); + FF_LIBRARY_LOAD_SYMBOL_MESSAGE(nm, nm_access_point_get_strength); + FF_LIBRARY_LOAD_SYMBOL_MESSAGE(nm, nm_access_point_get_max_bitrate); + FF_LIBRARY_LOAD_SYMBOL_MESSAGE(nm, nm_access_point_get_flags); + FF_LIBRARY_LOAD_SYMBOL_MESSAGE(nm, nm_access_point_get_wpa_flags); + FF_LIBRARY_LOAD_SYMBOL_MESSAGE(nm, nm_access_point_get_rsn_flags); + + FF_LIBRARY_LOAD_SYMBOL_MESSAGE(nm, g_type_check_instance_is_a); + FF_LIBRARY_LOAD_SYMBOL_MESSAGE(nm, g_bytes_get_size); + FF_LIBRARY_LOAD_SYMBOL_MESSAGE(nm, g_bytes_get_data); + FF_LIBRARY_LOAD_SYMBOL_MESSAGE(nm, g_free); + FF_LIBRARY_LOAD_SYMBOL_MESSAGE(nm, g_object_unref); + + NMClient* client = ffnm_client_new(NULL, NULL); + if(!client) + { + dlclose(nm); + return "Could not create NMClient"; + } + + /* Get all devices managed by NetworkManager */ + const GPtrArray* devices = ffnm_client_get_devices(client); + + /* Go through the array and process Wi-Fi devices */ + for (guint i = 0; i < devices->len; i++) + { + NMDevice *device = g_ptr_array_index(devices, i); + if (!({ + GTypeInstance *__inst = (GTypeInstance*) device; GType __t = ffnm_device_wifi_get_type(); gboolean __r; + if (!__inst) + __r = FALSE; + else if (__inst->g_class && __inst->g_class->g_type == __t) + __r = TRUE; + else + __r = ffg_type_check_instance_is_a (__inst, __t); + __r; + }) //NM_IS_DEVICE_WIFI(device) + ) + continue; + + 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.protocol); + ffStrbufInit(&item->conn.security); + item->conn.signalQuality = 0.0/0.0; + item->conn.rxRate = 0.0/0.0; + item->conn.txRate = 0.0/0.0; + + ffStrbufAppendS(&item->inf.description, ffnm_device_get_iface(device)); + NMDeviceState state = ffnm_device_get_state(device); + switch(state) + { + case NM_DEVICE_STATE_UNKNOWN: + ffStrbufAppendS(&item->inf.status, "Unknown"); + break; + case NM_DEVICE_STATE_UNMANAGED: + ffStrbufAppendS(&item->inf.status, "Unmanaged"); + break; + case NM_DEVICE_STATE_UNAVAILABLE: + ffStrbufAppendS(&item->inf.status, "Unavailable"); + break; + case NM_DEVICE_STATE_DISCONNECTED: + ffStrbufAppendS(&item->inf.status, "Disconnected"); + break; + case NM_DEVICE_STATE_PREPARE: + ffStrbufAppendS(&item->inf.status, "Prepare"); + break; + case NM_DEVICE_STATE_CONFIG: + ffStrbufAppendS(&item->inf.status, "Config"); + break; + case NM_DEVICE_STATE_NEED_AUTH: + ffStrbufAppendS(&item->inf.status, "Need auth"); + break; + case NM_DEVICE_STATE_IP_CONFIG: + ffStrbufAppendS(&item->inf.status, "IP config"); + break; + case NM_DEVICE_STATE_IP_CHECK: + ffStrbufAppendS(&item->inf.status, "IP check"); + break; + case NM_DEVICE_STATE_SECONDARIES: + ffStrbufAppendS(&item->inf.status, "Secondaries"); + break; + case NM_DEVICE_STATE_ACTIVATED: + ffStrbufAppendS(&item->inf.status, "Activated"); + break; + case NM_DEVICE_STATE_DEACTIVATING: + ffStrbufAppendS(&item->inf.status, "Deactivating"); + break; + case NM_DEVICE_STATE_FAILED: + ffStrbufAppendS(&item->inf.status, "Failed"); + break; + } + if(state != NM_DEVICE_STATE_ACTIVATED) + continue; + + NMAccessPoint* ap = ffnm_device_wifi_get_active_access_point((NMDeviceWifi *) device); + if (!ap) + continue; + + GBytes* activeSsid = ffnm_access_point_get_ssid(ap); + if (activeSsid) + { + char* ssid = ffnm_utils_ssid_to_utf8(ffg_bytes_get_data(activeSsid, NULL), ffg_bytes_get_size(activeSsid)); + ffStrbufAppendS(&item->conn.ssid, ssid); + ffg_free(ssid); + } + + ffStrbufAppendS(&item->conn.macAddress, ffnm_access_point_get_bssid(ap)); + item->conn.signalQuality = ffnm_access_point_get_strength(ap); + item->conn.rxRate = ffnm_access_point_get_max_bitrate(ap); + + if(instance->config.allowSlowOperations) + { + FF_STRBUF_AUTO_DESTROY output; + ffStrbufInit(&output); + if(!ffProcessAppendStdOut(&output, (char* const[]){ + "iw", + "dev", + item->inf.description.chars, + "link", + NULL + }) && ffParsePropLines(output.chars, "tx bitrate: ", &item->conn.protocol)) + { + if(ffStrbufContainS(&item->conn.protocol, " HE-MCS ")) + ffStrbufSetS(&item->conn.protocol, "802.11ax (Wi-Fi 6)"); + else if(ffStrbufContainS(&item->conn.protocol, " VHT-MCS ")) + ffStrbufSetS(&item->conn.protocol, "802.11ac (Wi-Fi 5)"); + else if(ffStrbufContainS(&item->conn.protocol, " MCS ")) + ffStrbufSetS(&item->conn.protocol, "802.11n (Wi-Fi 4)"); + else + ffStrbufSetS(&item->conn.protocol, "802.11a/b/g"); + } + } + + NM80211ApFlags flags = ffnm_access_point_get_flags(ap); + NM80211ApSecurityFlags wpaFlags = ffnm_access_point_get_wpa_flags(ap); + NM80211ApSecurityFlags rsnFlags = ffnm_access_point_get_rsn_flags(ap); + + if ((flags & NM_802_11_AP_FLAGS_PRIVACY) && (wpaFlags == NM_802_11_AP_SEC_NONE) + && (rsnFlags == NM_802_11_AP_SEC_NONE)) + ffStrbufAppendS(&item->conn.security, "WEP "); + if (wpaFlags != NM_802_11_AP_SEC_NONE) + ffStrbufAppendS(&item->conn.security, "WPA "); + if ((rsnFlags & NM_802_11_AP_SEC_KEY_MGMT_PSK) + || (rsnFlags & NM_802_11_AP_SEC_KEY_MGMT_802_1X)) { + ffStrbufAppendS(&item->conn.security, "WPA2 "); + } + if (rsnFlags & NM_802_11_AP_SEC_KEY_MGMT_SAE) { + ffStrbufAppendS(&item->conn.security, "WPA3 "); + } + if ((rsnFlags & NM_802_11_AP_SEC_KEY_MGMT_OWE) + || (rsnFlags & NM_802_11_AP_SEC_KEY_MGMT_OWE_TM)) { + ffStrbufAppendS(&item->conn.security, "OWE "); + } + if ((wpaFlags & NM_802_11_AP_SEC_KEY_MGMT_802_1X) + || (rsnFlags & NM_802_11_AP_SEC_KEY_MGMT_802_1X)) { + ffStrbufAppendS(&item->conn.security, "802.1X "); + } + if (!item->conn.security.length) + ffStrbufAppendS(&item->conn.security, "Insecure"); + else + ffStrbufTrimRight(&item->conn.security, ' '); + } + + ffg_object_unref(client); + dlclose(nm); + + return NULL; } -const char* ffDetectWifi(const FFinstance* instance, FFlist* result) -{ - FF_UNUSED(instance); +#endif +#include "common/io.h" + +#include +#include +#include +#include + +static const char* detectWifiWithIoctls(FF_MAYBE_UNUSED const FFinstance* instance, FFlist* result) +{ struct if_nameindex* infs = if_nameindex(); if(!infs) return "if_nameindex() failed"; @@ -34,28 +228,93 @@ const char* ffDetectWifi(const FFinstance* instance, FFlist* result) ffStrbufInit(&item->conn.status); ffStrbufInit(&item->conn.ssid); ffStrbufInit(&item->conn.macAddress); - ffStrbufInit(&item->conn.phyType); + ffStrbufInit(&item->conn.protocol); + ffStrbufInit(&item->conn.security); 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); + int sock = socket(AF_INET, SOCK_DGRAM | SOCK_CLOEXEC, 0); + if(sock < 0) + continue; - detectInf(i->if_index, item); + struct iwreq iwr; + strncpy(iwr.ifr_name, i->if_name, IFNAMSIZ); + ffStrbufEnsureFree(&item->conn.ssid, IW_ESSID_MAX_SIZE); + iwr.u.essid.pointer = (caddr_t) item->conn.ssid.chars; + iwr.u.essid.length = IW_ESSID_MAX_SIZE + 1; + iwr.u.essid.flags = 0; + if(ioctl(sock, SIOCGIWESSID, &iwr) >= 0) + ffStrbufRecalculateLength(&item->conn.ssid); + + if(ioctl(sock, SIOCGIWNAME, &iwr) >= 0) + { + if(strncasecmp(iwr.u.name, "IEEE ", 5) == 0) + ffStrbufSetS(&item->conn.protocol, iwr.u.name + 5); + else + ffStrbufSetS(&item->conn.protocol, iwr.u.name); + } + + if(ioctl(sock, SIOCGIWAP, &iwr) >= 0) + { + for(int i = 0; i < 6; ++i) + ffStrbufAppendF(&item->conn.macAddress, "%.2X-", (uint8_t) iwr.u.ap_addr.sa_data[i]); + ffStrbufTrimRight(&item->conn.macAddress, '-'); + } + + //FIXME: doesn't work + if(ioctl(sock, SIOCGIWSPY, &iwr) >= 0) + item->conn.signalQuality = iwr.u.qual.level; + + //FIXME: doesn't work + struct iw_encode_ext iwe; + iwr.u.data.pointer = &iwe; + iwr.u.data.length = sizeof(iwe); + iwr.u.data.flags = 0; + if(ioctl(sock, SIOCGIWENCODEEXT, &iwr) >= 0) + { + struct iw_encode_ext* iwe = iwr.u.data.pointer; + switch(iwe->alg) + { + case IW_ENCODE_ALG_WEP: + ffStrbufAppendS(&item->conn.security, "WEP"); + break; + case IW_ENCODE_ALG_TKIP: + ffStrbufAppendS(&item->conn.security, "TKIP"); + break; + case IW_ENCODE_ALG_CCMP: + ffStrbufAppendS(&item->conn.security, "CCMP"); + break; + case IW_ENCODE_ALG_PMK: + ffStrbufAppendS(&item->conn.security, "PMK"); + break; + case IW_ENCODE_ALG_AES_CMAC: + ffStrbufAppendS(&item->conn.security, "CMAC"); + break; + default: + ffStrbufAppendF(&item->conn.security, "Unknown (%d)", (int) iwe->alg); + break; + } + } + + close(sock); } if_freenameindex(infs); ffStrbufDestroy(&path); - if(result->length == 0) - return "No wifi interfaces found"; - return NULL; } + +const char* ffDetectWifi(const FFinstance* instance, FFlist* result) +{ + #ifdef FF_HAVE_LIBNM + if(!detectWifiWithLibnm(instance, result)) + return NULL; + #endif + + return detectWifiWithIoctls(instance, result); +} diff --git a/src/detection/wifi/wifi_windows.c b/src/detection/wifi/wifi_windows.c index 92322440f..70d7ea577 100644 --- a/src/detection/wifi/wifi_windows.c +++ b/src/detection/wifi/wifi_windows.c @@ -40,7 +40,7 @@ static void convertIfStateToString(WLAN_INTERFACE_STATE state, FFstrbuf* result) } } -const char* ffDetectWifi(FF_UNUSED_PARAM const FFinstance* instance, FFlist* result) +const char* ffDetectWifi(FF_MAYBE_UNUSED const FFinstance* instance, FFlist* result) { FF_LIBRARY_LOAD(wlanapi, NULL, "dlopen wlanapi"FF_LIBRARY_EXTENSION" failed", "wlanapi"FF_LIBRARY_EXTENSION, 1) FF_LIBRARY_LOAD_SYMBOL_MESSAGE(wlanapi, WlanOpenHandle) @@ -66,12 +66,6 @@ const char* ffDetectWifi(FF_UNUSED_PARAM const FFinstance* instance, FFlist* res 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]; @@ -82,13 +76,11 @@ const char* ffDetectWifi(FF_UNUSED_PARAM const FFinstance* instance, FFlist* res ffStrbufInit(&item->conn.status); ffStrbufInit(&item->conn.ssid); ffStrbufInit(&item->conn.macAddress); - ffStrbufInit(&item->conn.phyType); + ffStrbufInit(&item->conn.protocol); + ffStrbufInit(&item->conn.security); 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); @@ -121,40 +113,40 @@ const char* ffDetectWifi(FF_UNUSED_PARAM const FFinstance* instance, FFlist* res switch (connInfo->wlanAssociationAttributes.dot11PhyType) { case dot11_phy_type_fhss: - ffStrbufAppendS(&item->conn.phyType, "802.11 (FHSS)"); + ffStrbufAppendS(&item->conn.protocol, "802.11 (FHSS)"); break; case dot11_phy_type_dsss: - ffStrbufAppendS(&item->conn.phyType, "802.11 (DSSS)"); + ffStrbufAppendS(&item->conn.protocol, "802.11 (DSSS)"); break; case dot11_phy_type_irbaseband: - ffStrbufAppendS(&item->conn.phyType, "802.11 (IR)"); + ffStrbufAppendS(&item->conn.protocol, "802.11 (IR)"); break; case dot11_phy_type_ofdm: - ffStrbufAppendS(&item->conn.phyType, "802.11a"); + ffStrbufAppendS(&item->conn.protocol, "802.11a"); break; case dot11_phy_type_hrdsss: - ffStrbufAppendS(&item->conn.phyType, "802.11b"); + ffStrbufAppendS(&item->conn.protocol, "802.11b"); break; case dot11_phy_type_erp: - ffStrbufAppendS(&item->conn.phyType, "802.11g"); + ffStrbufAppendS(&item->conn.protocol, "802.11g"); break; case dot11_phy_type_ht: - ffStrbufAppendS(&item->conn.phyType, "802.11n (Wi-Fi 4)"); + ffStrbufAppendS(&item->conn.protocol, "802.11n (Wi-Fi 4)"); break; case 8 /*dot11_phy_type_vht*/: - ffStrbufAppendS(&item->conn.phyType, "802.11ac (Wi-Fi 5)"); + ffStrbufAppendS(&item->conn.protocol, "802.11ac (Wi-Fi 5)"); break; case 9 /*dot11_phy_type_dmg*/: - ffStrbufAppendS(&item->conn.phyType, "802.11ad (WiGig)"); + ffStrbufAppendS(&item->conn.protocol, "802.11ad (WiGig)"); break; case 10 /*dot11_phy_type_he*/: - ffStrbufAppendS(&item->conn.phyType, "802.11ax (Wi-Fi 6)"); + ffStrbufAppendS(&item->conn.protocol, "802.11ax (Wi-Fi 6)"); break; case 11 /*dot11_phy_type_eht*/: - ffStrbufAppendS(&item->conn.phyType, "802.11be (Wi-Fi 7)"); + ffStrbufAppendS(&item->conn.protocol, "802.11be (Wi-Fi 7)"); break; default: - ffStrbufAppendF(&item->conn.phyType, "Unknown (%u)", (unsigned)connInfo->wlanAssociationAttributes.dot11PhyType); + ffStrbufAppendF(&item->conn.protocol, "Unknown (%u)", (unsigned)connInfo->wlanAssociationAttributes.dot11PhyType); break; } @@ -162,93 +154,53 @@ const char* ffDetectWifi(FF_UNUSED_PARAM const FFinstance* instance, FFlist* res 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) + if(connInfo->wlanSecurityAttributes.bSecurityEnabled) { - 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; + switch (connInfo->wlanSecurityAttributes.dot11AuthAlgorithm) + { + case DOT11_AUTH_ALGO_80211_OPEN: + ffStrbufAppendS(&item->conn.security, "802.11 Open"); + break; + case DOT11_AUTH_ALGO_80211_SHARED_KEY: + ffStrbufAppendS(&item->conn.security, "802.11 Shared"); + break; + case DOT11_AUTH_ALGO_WPA: + ffStrbufAppendS(&item->conn.security, "WPA"); + break; + case DOT11_AUTH_ALGO_WPA_PSK: + ffStrbufAppendS(&item->conn.security, "WPA-PSK"); + break; + case DOT11_AUTH_ALGO_WPA_NONE: + ffStrbufAppendS(&item->conn.security, "WPA-None"); + break; + case DOT11_AUTH_ALGO_RSNA: + ffStrbufAppendS(&item->conn.security, "WPA2"); + break; + case DOT11_AUTH_ALGO_RSNA_PSK: + ffStrbufAppendS(&item->conn.security, "WPA2-PSK"); + break; + case 8 /* DOT11_AUTH_ALGO_WPA3 */: + ffStrbufAppendS(&item->conn.security, "WPA3"); + break; + case 9 /* DOT11_AUTH_ALGO_WPA3_SAE */: + ffStrbufAppendS(&item->conn.security, "WPA3-SAE"); + break; + case 10 /* DOT11_AUTH_ALGO_OWE */: + ffStrbufAppendS(&item->conn.security, "OWE"); + break; + case 11 /* DOT11_AUTH_ALGO_WPA3_ENT */: + ffStrbufAppendS(&item->conn.security, "OWE-ENT"); + break; + default: + ffStrbufAppendF(&item->conn.security, "Unknown (%u)", (unsigned)connInfo->wlanSecurityAttributes.dot11AuthAlgorithm); + break; + } + if(connInfo->wlanSecurityAttributes.bOneXEnabled) + ffStrbufAppendS(&item->conn.security, " 802.11X"); } + else + ffStrbufAppendS(&item->conn.security, "Insecure"); + ffWlanFreeMemory(connInfo); } diff --git a/src/detection/wmtheme/wmtheme_apple.m b/src/detection/wmtheme/wmtheme_apple.m index d0b099790..437d6ed97 100644 --- a/src/detection/wmtheme/wmtheme_apple.m +++ b/src/detection/wmtheme/wmtheme_apple.m @@ -9,7 +9,7 @@ static bool detectQuartzCompositor(FFinstance* instance, FFstrbuf* themeOrError) FF_UNUSED(instance); NSError* error; - NSString* fileName = [NSString stringWithFormat:@"file://%s/Library/Preferences/.GlobalPreferences.plist", instance->state.passwd->pw_dir]; + NSString* fileName = [NSString stringWithFormat:@"file://%s/Library/Preferences/.GlobalPreferences.plist", instance->state.platform.homeDir.chars]; NSDictionary* dict = [NSDictionary dictionaryWithContentsOfURL:[NSURL URLWithString:fileName] error:&error]; if(error) diff --git a/src/detection/wmtheme/wmtheme_linux.c b/src/detection/wmtheme/wmtheme_linux.c index 19765c79a..6efba12c5 100644 --- a/src/detection/wmtheme/wmtheme_linux.c +++ b/src/detection/wmtheme/wmtheme_linux.c @@ -131,9 +131,9 @@ static bool detectOpenbox(FFinstance* instance, const FFstrbuf* dePrettyName, FF { FFstrbuf absolutePath; ffStrbufInitA(&absolutePath, 64); - ffStrbufAppendS(&absolutePath, instance->state.passwd->pw_dir); - ffStrbufAppendC(&absolutePath, '/'); + ffStrbufAppend(&absolutePath, &instance->state.platform.homeDir); + //TODO: use config dirs if(ffStrbufIgnCaseCompS(dePrettyName, "LXQT") == 0) ffStrbufAppendS(&absolutePath, ".config/openbox/lxqt-rc.xml"); else if(ffStrbufIgnCaseCompS(dePrettyName, "LXDE") == 0) diff --git a/src/fastfetch.c b/src/fastfetch.c index fd36ebc1d..18c130ef2 100644 --- a/src/fastfetch.c +++ b/src/fastfetch.c @@ -174,12 +174,13 @@ static inline void printCommandHelp(const char* command) "User shell version" ); } - else if(strcasecmp(command, "resolution-format") == 0) + else if(strcasecmp(command, "display-format") == 0) { - constructAndPrintCommandHelpFormat("resolution", "{}x{} @ {}Hz", 3, + constructAndPrintCommandHelpFormat("display", "{}x{} @ {}Hz", 4, "Screen width", "Screen height", - "Screen refresh rate" + "Screen refresh rate", + "Screen brightness" ); } else if(strcasecmp(command, "de-format") == 0) @@ -287,12 +288,13 @@ static inline void printCommandHelp(const char* command) } else if(strcasecmp(command, "gpu-format") == 0) { - constructAndPrintCommandHelpFormat("gpu", "{} {}", 5, + constructAndPrintCommandHelpFormat("gpu", "{} {}", 6, "GPU vendor", "GPU name", "GPU driver", "GPU temperature", - "GPU core count" + "GPU core count", + "GPU type" ); } else if(strcasecmp(command, "memory-format") == 0) @@ -371,13 +373,11 @@ static inline void printCommandHelp(const char* command) "Connection status", "Connection SSID", "Connection mac address", - "Connection PHY type", + "Connection protocol", "Connection signal quality (percentage)", "Connection RX rate", "Connection TX rate", - "Security enabled", - "Security 802.1X enabled", - "Security algorithm" + "Connection Security algorithm" ); } else if(strcasecmp(command, "player-format") == 0) @@ -525,17 +525,29 @@ static inline void listAvailablePresetsFromFolder(FFstrbuf* folder, uint8_t inde static inline void listAvailablePresets(FFinstance* instance) { - FFstrbuf folder; - ffStrbufInitA(&folder, 64); + FF_LIST_FOR_EACH(FFstrbuf, path, instance->state.platform.dataDirs) + { + ffStrbufAppendS(path, "fastfetch/presets/"); + listAvailablePresetsFromFolder(path, 0, NULL); + } +} - ffStrbufSetS(&folder, instance->state.passwd->pw_dir); - ffStrbufAppendS(&folder, "/.local/share/fastfetch/presets/"); - listAvailablePresetsFromFolder(&folder, 0, NULL); +static void listConfigPaths(FFinstance* instance) +{ + FF_LIST_FOR_EACH(FFstrbuf, folder, instance->state.platform.configDirs) + { + ffStrbufAppendS(folder, "fastfetch/config.conf"); + printf("%s%s\n", folder->chars, ffFileExists(folder->chars, S_IFREG) ? " (*)" : ""); + } +} - ffStrbufSetS(&folder, FASTFETCH_TARGET_DIR_USR"/share/fastfetch/presets/"); - listAvailablePresetsFromFolder(&folder, 0, NULL); - - ffStrbufDestroy(&folder); +static void listDataPaths(FFinstance* instance) +{ + FF_LIST_FOR_EACH(FFstrbuf, folder, instance->state.platform.dataDirs) + { + ffStrbufAppendS(folder, "fastfetch/"); + puts(folder->chars); + } } static void parseOption(FFinstance* instance, FFdata* data, const char* key, const char* value); @@ -603,6 +615,25 @@ static bool parseConfigFile(FFinstance* instance, FFdata* data, const char* path return true; } +static void generateConfigFile(FFinstance* instance, bool force) +{ + FFstrbuf* filename = (FFstrbuf*) ffListGet(&instance->state.platform.configDirs, 0); + // Paths generated in `init.c/initConfigDirs` end with `/` + ffStrbufAppendS(filename, "fastfetch/config.conf"); + + if (!force && ffFileExists(filename->chars, S_IFREG)) + { + fprintf(stderr, "Config file exists in `%s`, use `--gen-config-force` to overwrite\n", filename->chars); + exit(1); + } + else + { + ffWriteFileData(filename->chars, sizeof(FASTFETCH_DATATEXT_CONFIG_USER), FASTFETCH_DATATEXT_CONFIG_USER); + printf("A sample config file has been written in `%s`\n", filename->chars); + exit(0); + } +} + static void optionParseConfigFile(FFinstance* instance, FFdata* data, const char* key, const char* value) { if(value == NULL) @@ -616,37 +647,29 @@ static void optionParseConfigFile(FFinstance* instance, FFdata* data, const char if(parseConfigFile(instance, data, value)) return; - //Try to load as an user preset + //Try to load as a relative path - FFstrbuf filename; - ffStrbufInitA(&filename, 64); + FFstrbuf absolutePath; + ffStrbufInitA(&absolutePath, 128); - ffStrbufAppendS(&filename, instance->state.passwd->pw_dir); - ffStrbufAppendS(&filename, "/.local/share/fastfetch/presets/"); - ffStrbufAppendS(&filename, value); - - if(parseConfigFile(instance, data, filename.chars)) + FF_LIST_FOR_EACH(FFstrbuf, path, instance->state.platform.dataDirs) { - ffStrbufDestroy(&filename); - return; + //We need to copy it, because if a config file loads a config file, the value of path must be unchanged + ffStrbufSet(&absolutePath, path); + ffStrbufAppendS(&absolutePath, "fastfetch/presets/"); + ffStrbufAppendS(&absolutePath, value); + + bool success = parseConfigFile(instance, data, absolutePath.chars); + + if(success) + return; } - - //Try to load as a system preset - - ffStrbufSetS(&filename, FASTFETCH_TARGET_DIR_USR"/share/fastfetch/presets/"); - ffStrbufAppendS(&filename, value); - - if(parseConfigFile(instance, data, filename.chars)) - { - ffStrbufDestroy(&filename); - return; - } + ffStrbufDestroy(&absolutePath); //File not found fprintf(stderr, "Error: couldn't find config: %s\n", value); - ffStrbufDestroy(&filename); exit(414); } @@ -836,7 +859,35 @@ static void parseOption(FFinstance* instance, FFdata* data, const char* key, con } else if(strcasecmp(key, "-v") == 0 || strcasecmp(key, "--version") == 0) { - puts("fastfetch "FASTFETCH_PROJECT_VERSION""FASTFETCH_PROJECT_VERSION_TWEAK); + #ifndef NDEBUG + #define FF_BUILD_TYPE "-debug" + #else + #define FF_BUILD_TYPE + #endif + + #if defined(__x86_64__) || defined(__x86_64) || defined(__amd64__) || defined(__amd64) + #define FF_ARCHITECTURE "x86_64" + #elif defined(__i386__) || defined(__i386) || defined(__i486__) || defined(__i486) || defined(__i586__) || defined(__i586) || defined(__i686__) || defined(__i686) + #define FF_ARCHITECTURE "i386" + #elif defined(__aarch64__) + #define FF_ARCHITECTURE "aarch64" + #elif defined(__arm__) + #define FF_ARCHITECTURE "arm" + #elif defined(__mips__) + #define FF_ARCHITECTURE "mips" + #elif defined(__powerpc__) || defined(__powerpc) + #define FF_ARCHITECTURE "powerpc" + #elif defined(__riscv__) || defined(__riscv) + #define FF_ARCHITECTURE "riscv" + #elif + #define FF_ARCHITECTURE "unknown" + #endif + + puts("fastfetch " FASTFETCH_PROJECT_VERSION FASTFETCH_PROJECT_VERSION_TWEAK FF_BUILD_TYPE " (" FF_ARCHITECTURE ")"); + + #undef FF_ARCHITECTURE + #undef FF_BUILD_TYPE + exit(0); } else if(strcasecmp(key, "--version-raw") == 0) @@ -883,6 +934,16 @@ static void parseOption(FFinstance* instance, FFdata* data, const char* key, con listAvailablePresets(instance); exit(0); } + else if(strcasecmp(subkey, "-config-paths") == 0) + { + listConfigPaths(instance); + exit(0); + } + else if(strcasecmp(subkey, "-data-paths") == 0) + { + listDataPaths(instance); + exit(0); + } else if(strcasecmp(subkey, "-features") == 0) { ffListFeatures(); @@ -912,6 +973,10 @@ static void parseOption(FFinstance* instance, FFdata* data, const char* key, con 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, "--gen-config") == 0) + generateConfigFile(instance, false); + else if(strcasecmp(key, "--gen-config-force") == 0) + generateConfigFile(instance, true); else if(strcasecmp(key, "--thread") == 0 || strcasecmp(key, "--multithreading") == 0) instance->config.multithreading = optionParseBoolean(value); else if(strcasecmp(key, "--stat") == 0) @@ -939,8 +1004,10 @@ static void parseOption(FFinstance* instance, FFdata* data, const char* key, con //this is usally wanted when using the none logo if(strcasecmp(value, "none") == 0) { + instance->config.logo.paddingTop = 0; instance->config.logo.paddingRight = 0; instance->config.logo.paddingLeft = 0; + instance->config.logo.type = FF_LOGO_TYPE_NONE; } } else if(startsWith(key, "--logo")) @@ -957,7 +1024,10 @@ static void parseOption(FFinstance* instance, FFdata* data, const char* key, con "data-raw", FF_LOGO_TYPE_DATA_RAW, "sixel", FF_LOGO_TYPE_IMAGE_SIXEL, "kitty", FF_LOGO_TYPE_IMAGE_KITTY, + "iterm", FF_LOGO_TYPE_IMAGE_ITERM, "chafa", FF_LOGO_TYPE_IMAGE_CHAFA, + "raw", FF_LOGO_TYPE_IMAGE_RAW, + "none", FF_LOGO_TYPE_NONE, NULL ); } @@ -985,6 +1055,8 @@ static void parseOption(FFinstance* instance, FFdata* data, const char* key, con instance->config.logo.paddingLeft = padding; instance->config.logo.paddingRight = padding; } + else if(strcasecmp(subkey, "-padding-top") == 0) + instance->config.logo.paddingTop = optionParseUInt32(key, value); else if(strcasecmp(subkey, "-padding-left") == 0) instance->config.logo.paddingLeft = optionParseUInt32(key, value); else if(strcasecmp(subkey, "-padding-right") == 0) @@ -1036,6 +1108,11 @@ static void parseOption(FFinstance* instance, FFdata* data, const char* key, con optionParseString(key, value, &instance->config.logo.source); instance->config.logo.type = FF_LOGO_TYPE_IMAGE_ITERM; } + else if(strcasecmp(key, "--raw") == 0) + { + optionParseString(key, value, &instance->config.logo.source); + instance->config.logo.type = FF_LOGO_TYPE_IMAGE_RAW; + } else if(strcasecmp(key, "--chafa-fg-only") == 0) instance->config.logo.chafaFgOnly = optionParseBoolean(value); else if(strcasecmp(key, "--chafa-symbols") == 0) @@ -1098,7 +1175,8 @@ static void parseOption(FFinstance* instance, FFdata* data, const char* key, con 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, "display", &instance->config.display)) {} + else if(optionParseModuleArgs(key, value, "brightness", &instance->config.brightness)) {} 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)) {} @@ -1184,6 +1262,8 @@ static void parseOption(FFinstance* instance, FFdata* data, const char* key, con optionParseString(key, value, &instance->config.libcJSON); else if(strcasecmp(subkey, "-wlanapi") == 0) optionParseString(key, value, &instance->config.libwlanapi); + else if(strcasecmp(subkey, "-nm") == 0) + optionParseString(key, value, &instance->config.libnm); else goto error; } @@ -1198,14 +1278,26 @@ static void parseOption(FFinstance* instance, FFdata* data, const char* key, con instance->config.gpuTemp = optionParseBoolean(value); else if(strcasecmp(key, "--battery-temp") == 0) instance->config.batteryTemp = optionParseBoolean(value); + else if(strcasecmp(key, "--gpu-hide-integrated") == 0) + instance->config.gpuHideIntegrated = optionParseBoolean(value); + else if(strcasecmp(key, "--gpu-hide-discrete") == 0) + instance->config.gpuHideDiscrete = optionParseBoolean(value); else if(strcasecmp(key, "--title-fqdn") == 0) instance->config.titleFQDN = optionParseBoolean(value); + else if(strcasecmp(key, "--shell-version") == 0) + instance->config.shellVersion = optionParseBoolean(value); + else if(strcasecmp(key, "--terminal-version") == 0) + instance->config.terminalVersion = optionParseBoolean(value); else if(strcasecmp(key, "--disk-folders") == 0) optionParseString(key, value, &instance->config.diskFolders); 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, "--disk-show-subvolumes") == 0) + instance->config.diskShowSubvolumes = optionParseBoolean(value); + else if(strcasecmp(key, "--disk-show-unknown") == 0) + instance->config.diskShowUnknown = optionParseBoolean(value); else if(strcasecmp(key, "--battery-dir") == 0) optionParseString(key, value, &instance->config.batteryDir); else if(strcasecmp(key, "--separator-string") == 0) @@ -1242,6 +1334,20 @@ static void parseOption(FFinstance* instance, FFdata* data, const char* key, con } else if(strcasecmp(key, "--percent-type") == 0) instance->config.percentType = optionParseUInt32(key, value); + else if(strcasecmp(key, "--command-shell") == 0) + optionParseString(key, value, &instance->config.commandShell); + else if(strcasecmp(key, "--command-key") == 0) + { + FFstrbuf* result = (FFstrbuf*) ffListAdd(&instance->config.commandKeys); + ffStrbufInit(result); + optionParseString(key, value, result); + } + else if(strcasecmp(key, "--command-text") == 0) + { + FFstrbuf* result = (FFstrbuf*) ffListAdd(&instance->config.commandTexts); + ffStrbufInit(result); + optionParseString(key, value, result); + } ////////////////// //Unknown option// @@ -1255,25 +1361,20 @@ error: } } -static void parseConfigFileSystem(FFinstance* instance, FFdata* data) +static void parseConfigFiles(FFinstance* instance, FFdata* data) { - parseConfigFile(instance, data, FASTFETCH_TARGET_DIR_INSTALL_SYSCONF"/fastfetch/config.conf"); -} + for(uint32_t i = instance->state.platform.configDirs.length; i > 0; --i) + { + if(!data->loadUserConfig) + return; -static void parseConfigFileUser(FFinstance* instance, FFdata* data) -{ - if(!data->loadUserConfig) - return; + FFstrbuf* dir = ffListGet(&instance->state.platform.configDirs, i - 1); + uint32_t dirLength = dir->length; - FFstrbuf* filename = ffListGet(&instance->state.configDirs, 0); - uint32_t filenameLength = filename->length; - - ffStrbufAppendS(filename, "/fastfetch/config.conf"); - - if(!parseConfigFile(instance, data, filename->chars)) - ffWriteFileData(filename->chars, sizeof(FASTFETCH_DATATEXT_CONFIG_USER), FASTFETCH_DATATEXT_CONFIG_USER); - - ffStrbufSubstrBefore(filename, filenameLength); + ffStrbufAppendS(dir, "fastfetch/config.conf"); + parseConfigFile(instance, data, dir->chars); + ffStrbufSubstrBefore(dir, dirLength); + } } static void parseArguments(FFinstance* instance, FFdata* data, int argc, const char** argv) @@ -1320,6 +1421,8 @@ static void parseStructureCommand(FFinstance* instance, FFdata* data, const char ffPrintBios(instance); else if(strcasecmp(line, "board") == 0) ffPrintBoard(instance); + else if(strcasecmp(line, "brightness") == 0) + ffPrintBrightness(instance); else if(strcasecmp(line, "chassis") == 0) ffPrintChassis(instance); else if(strcasecmp(line, "kernel") == 0) @@ -1332,8 +1435,8 @@ static void parseStructureCommand(FFinstance* instance, FFdata* data, const char ffPrintPackages(instance); else if(strcasecmp(line, "shell") == 0) ffPrintShell(instance); - else if(strcasecmp(line, "resolution") == 0) - ffPrintResolution(instance); + else if(strcasecmp(line, "display") == 0) + ffPrintDisplay(instance); else if(strcasecmp(line, "desktopenvironment") == 0 || strcasecmp(line, "de") == 0) ffPrintDesktopEnvironment(instance); else if(strcasecmp(line, "windowmanager") == 0 || strcasecmp(line, "wm") == 0) @@ -1398,6 +1501,8 @@ static void parseStructureCommand(FFinstance* instance, FFdata* data, const char ffPrintOpenCL(instance); else if(strcasecmp(line, "users") == 0) ffPrintUsers(instance); + else if(strcasecmp(line, "command") == 0) + ffPrintCommand(instance); else ffPrintErrorString(instance, line, 0, NULL, NULL, ""); } @@ -1413,8 +1518,8 @@ int main(int argc, const char** argv) ffStrbufInitA(&data.structure, 256); data.loadUserConfig = true; - parseConfigFileSystem(&instance, &data); - parseConfigFileUser(&instance, &data); + if(!getenv("NO_CONFIG")) + parseConfigFiles(&instance, &data); parseArguments(&instance, &data, argc, argv); //If we don't have a custom structure, use the default one @@ -1432,6 +1537,10 @@ int main(int argc, const char** argv) ffStart(&instance); + #if defined(_WIN32) && defined(FF_ENABLE_BUFFER) + fflush(stdout); + #endif + //Parse the structure and call the modules uint32_t startIndex = 0; while (startIndex < data.structure.length) diff --git a/src/fastfetch.h b/src/fastfetch.h index a99eadc44..69121e4c9 100644 --- a/src/fastfetch.h +++ b/src/fastfetch.h @@ -8,20 +8,13 @@ #include #include -#ifndef _WIN32 - #include - #include -#else - #include "util/windows/pwd.h" - #include "util/windows/utsname.h" -#endif - #include "util/FFstrbuf.h" #include "util/FFlist.h" +#include "util/platform/FFPlatform.h" static inline void ffUnused(int dummy, ...) { (void) dummy; } #define FF_UNUSED(...) ffUnused(0, __VA_ARGS__); -#define FF_UNUSED_PARAM __attribute__ ((__unused__)) +#define FF_MAYBE_UNUSED __attribute__ ((__unused__)) #define FASTFETCH_LOGO_MAX_COLORS 9 //two digits would make parsing much more complicated (index 1 - 9) @@ -37,6 +30,8 @@ typedef enum FFLogoType 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 + FF_LOGO_TYPE_IMAGE_RAW, //image file, printed as raw binary string + FF_LOGO_TYPE_NONE, //--logo none } FFLogoType; typedef enum FFBinaryPrefixType @@ -70,6 +65,7 @@ typedef struct FFconfig FFstrbuf colors[FASTFETCH_LOGO_MAX_COLORS]; uint32_t width; uint32_t height; + uint32_t paddingTop; uint32_t paddingLeft; uint32_t paddingRight; bool printRemaining; @@ -103,14 +99,15 @@ typedef struct FFconfig FFModuleArgs os; FFModuleArgs host; FFModuleArgs bios; - FFModuleArgs chassis; FFModuleArgs board; + FFModuleArgs brightness; + FFModuleArgs chassis; FFModuleArgs kernel; FFModuleArgs uptime; FFModuleArgs processes; FFModuleArgs packages; FFModuleArgs shell; - FFModuleArgs resolution; + FFModuleArgs display; FFModuleArgs de; FFModuleArgs wifi; FFModuleArgs wm; @@ -166,16 +163,25 @@ typedef struct FFconfig FFstrbuf libcJSON; FFstrbuf libfreetype; FFstrbuf libwlanapi; + FFstrbuf libnm; bool cpuTemp; bool gpuTemp; bool batteryTemp; + bool gpuHideIntegrated; + bool gpuHideDiscrete; + bool titleFQDN; + bool shellVersion; + bool terminalVersion; + FFstrbuf diskFolders; bool diskShowRemovable; bool diskShowHidden; + bool diskShowUnknown; + bool diskShowSubvolumes; FFstrbuf batteryDir; @@ -197,6 +203,10 @@ typedef struct FFconfig FFstrbuf playerName; uint32_t percentType; + + FFstrbuf commandShell; + FFlist commandKeys; + FFlist commandTexts; } FFconfig; typedef struct FFstate @@ -205,10 +215,7 @@ typedef struct FFstate uint32_t logoHeight; uint32_t keysHeight; - struct passwd* passwd; - struct utsname utsname; - - FFlist configDirs; + FFPlatform platform; } FFstate; typedef struct FFinstance @@ -268,7 +275,8 @@ void ffPrintUptime(FFinstance* instance); void ffPrintProcesses(FFinstance* instance); void ffPrintPackages(FFinstance* instance); void ffPrintShell(FFinstance* instance); -void ffPrintResolution(FFinstance* instance); +void ffPrintDisplay(FFinstance* instance); +void ffPrintBrightness(FFinstance* instance); void ffPrintDesktopEnvironment(FFinstance* instance); void ffPrintWM(FFinstance* instance); void ffPrintWMTheme(FFinstance* instance); @@ -301,5 +309,6 @@ void ffPrintVulkan(FFinstance* instance); void ffPrintOpenGL(FFinstance* instance); void ffPrintOpenCL(FFinstance* instance); void ffPrintUsers(FFinstance* instance); +void ffPrintCommand(FFinstance* instance); #endif diff --git a/src/flashfetch.c b/src/flashfetch.c index 196e94bb5..0c24732e3 100644 --- a/src/flashfetch.c +++ b/src/flashfetch.c @@ -30,7 +30,8 @@ int main(int argc, char** argv) //ffPrintProcesses(&instance); ffPrintPackages(&instance); ffPrintShell(&instance); - ffPrintResolution(&instance); + ffPrintDisplay(&instance); + // ffPrintBrightness(&instance); ffPrintDesktopEnvironment(&instance); ffPrintWM(&instance); ffPrintWMTheme(&instance); diff --git a/src/logo/builtin.c b/src/logo/builtin.c index d13b7e4f8..09d13a2f0 100644 --- a/src/logo/builtin.c +++ b/src/logo/builtin.c @@ -1871,6 +1871,68 @@ static const FFlogo* getLogoPopSmall() FF_LOGO_COLOR_TITLE("36"); //cyan FF_LOGO_RETURN } +static const FFlogo* getLogoRaspbian() +{ + FF_LOGO_INIT + FF_LOGO_NAMES("raspbian", "raspi", "raspberrypi" "raspberrypios" "pios") + FF_LOGO_LINES( + " $2`.::///+:/-. --///+//-:`\n" + " `+oooooooooooo: `+oooooooooooo:\n" + " /oooo++//ooooo: ooooo+//+ooooo.\n" + " `+ooooooo:-:oo- +o+::/ooooooo:\n" + " `:oooooooo+`` `.oooooooo+-\n" + " `:++ooo/. :+ooo+/.`$1\n" + " ...` `.----.` ``..\n" + " .::::-``:::::::::.`-:::-`\n" + " -:::-` .:::::::-` `-:::-\n" + " `::. `.--.` `` `.---.``.::`\n" + " .::::::::` -::::::::` `\n" + " .::` .:::::::::- `::::::::::``::.\n" + "-:::` ::::::::::. ::::::::::.`:::-\n" + ":::: -::::::::. `-:::::::: ::::\n" + "-::- .-:::-.``....``.-::-. -::-\n" + " .. `` .::::::::. `..`..\n" + " -:::-` -::::::::::` .:::::`\n" + " :::::::` -::::::::::` :::::::.\n" + " .::::::: -::::::::. ::::::::\n" + " `-:::::` ..--.` ::::::.\n" + " `...` `...--..` `...`\n" + " .::::::::::\n" + " `.-::::-`" + ) + FF_LOGO_COLORS( + "31", //red + "32" //green + ) + FF_LOGO_COLOR_KEYS("31"); //red + FF_LOGO_COLOR_TITLE("32"); //green + FF_LOGO_RETURN +} + +static const FFlogo* getLogoRaspbianSmall() +{ + FF_LOGO_INIT + FF_LOGO_NAMES("raspbian_small", "raspi_small", "raspberrypi_small" "raspberrypios_small" "pios_small") + FF_LOGO_LINES( + " $2.~~. .~~.\n" + " '. \\ ' ' / .'$1\n" + " .~ .~~~..~.\n" + " : .~.'~'.~. :\n" + " ~ ( ) ( ) ~\n" + "( : '~'.~.'~' : )\n" + " ~ .~ ( ) ~. ~\n" + " ( : '~' : )\n" + " '~ .~~~. ~'\n" + " '~'" + ) + FF_LOGO_COLORS( + "31", //red + "32" //green + ) + FF_LOGO_COLOR_KEYS("31"); //red + FF_LOGO_COLOR_TITLE("32"); //green + FF_LOGO_RETURN +} static const FFlogo* getLogoReborn() { @@ -2430,6 +2492,8 @@ GetLogoMethod* ffLogoBuiltinGetAll() getLogoPopSmall, getLogoParabola, getLogoParabolaSmall, + getLogoRaspbian, + getLogoRaspbianSmall, getLogoReborn, getLogoRebornSmall, getLogoRedHatEnterpriseLinux, diff --git a/src/logo/image/image.c b/src/logo/image/image.c index aed67ca6a..b2bfff6b5 100644 --- a/src/logo/image/image.c +++ b/src/logo/image/image.c @@ -2,6 +2,10 @@ #include "common/io.h" #include "common/printing.h" +#ifdef __APPLE__ + #include +#endif + static FFstrbuf base64Encode(FFstrbuf* in) { const char* base64Chars = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/"; @@ -30,24 +34,31 @@ static FFstrbuf base64Encode(FFstrbuf* in) static bool printImageIterm(FFinstance* instance) { if(instance->config.logo.width == 0 || instance->config.logo.height == 0) + { + fputs("Logo: `iterm` protocol only works when both `--logo-width` and `--logo-height` being specified\n", stderr); return false; + } FFstrbuf buf; ffStrbufInit(&buf); if(!ffAppendFileBuffer(instance->config.logo.source.chars, &buf)) + { + fputs("Logo: Failed to load image file\n", stderr); return false; + } ffPrintCharTimes(' ', instance->config.logo.paddingLeft); + ffPrintCharTimes('\n', instance->config.logo.paddingTop); FFstrbuf base64 = base64Encode(&buf); + instance->state.logoWidth = instance->config.logo.width + instance->config.logo.paddingLeft + instance->config.logo.paddingRight; + instance->state.logoHeight = instance->config.logo.paddingTop + instance->config.logo.height; 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 + (unsigned) instance->state.logoHeight ); - 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); @@ -58,6 +69,7 @@ static bool printImageIterm(FFinstance* instance) static bool printImageKittyDirect(FFinstance* instance) { ffPrintCharTimes(' ', instance->config.logo.paddingLeft); + ffPrintCharTimes('\n', instance->config.logo.paddingTop); 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, @@ -66,7 +78,7 @@ static bool printImageKittyDirect(FFinstance* instance) ); instance->state.logoWidth = instance->config.logo.width + instance->config.logo.paddingLeft + instance->config.logo.paddingRight; - instance->state.logoHeight = instance->config.logo.height; + instance->state.logoHeight = instance->config.logo.paddingTop + instance->config.logo.height; ffStrbufDestroy(&base64); @@ -101,6 +113,7 @@ static inline char* realpath(const char* restrict file_name, char* restrict reso resolved_name[1] = resolved_name[0]; // Drive Name resolved_name[0] = '/'; } + return result; } #endif @@ -199,7 +212,7 @@ static void printImagePixels(FFinstance* instance, FFLogoRequestData* requestDat //Calculate character dimensions instance->state.logoWidth = requestData->logoCharacterWidth + instance->config.logo.paddingLeft + instance->config.logo.paddingRight; - instance->state.logoHeight = requestData->logoCharacterHeight; + instance->state.logoHeight = requestData->logoCharacterHeight + instance->config.logo.paddingTop; if(requestData->type == FF_LOGO_TYPE_IMAGE_KITTY) instance->state.logoHeight -= 1; @@ -213,6 +226,7 @@ static void printImagePixels(FFinstance* instance, FFLogoRequestData* requestDat writeCacheUint32(requestData, instance->state.logoHeight, FF_CACHE_FILE_HEIGHT); //Write result to stdout + ffPrintCharTimes('\n', instance->config.logo.paddingTop); ffPrintCharTimes(' ', instance->config.logo.paddingLeft); fflush(stdout); ffWriteFDBuffer(STDOUT_FILENO, result); @@ -594,6 +608,7 @@ static bool printCachedPixel(FFinstance* instance, FFLogoRequestData* requestDat if(fd == -1) return false; + ffPrintCharTimes('\n', instance->config.logo.paddingTop); ffPrintCharTimes(' ', instance->config.logo.paddingLeft); fflush(stdout); @@ -605,7 +620,7 @@ static bool printCachedPixel(FFinstance* instance, FFLogoRequestData* requestDat close(fd); instance->state.logoWidth = requestData->logoCharacterWidth + instance->config.logo.paddingLeft + instance->config.logo.paddingRight; - instance->state.logoHeight = requestData->logoCharacterHeight; + instance->state.logoHeight = requestData->logoCharacterHeight + instance->config.logo.paddingTop; //Go to upper left corner fputs("\033[9999999D", stdout); @@ -676,20 +691,8 @@ static bool printImageIfExistsSlowPath(FFinstance* instance, FFLogoType type) 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); - - #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, '/'); - + ffStrbufInit(&requestData.cacheDir); + ffStrbufAppend(&requestData.cacheDir, &instance->state.platform.cacheDir); ffStrbufAppendS(&requestData.cacheDir, "fastfetch"); ffStrbufEnsureFree(&requestData.cacheDir, PATH_MAX); diff --git a/src/logo/logo.c b/src/logo/logo.c index 3c48e0854..60cbdf8bb 100644 --- a/src/logo/logo.c +++ b/src/logo/logo.c @@ -8,13 +8,27 @@ #include #include +static void ffLogoPrintCharsRaw(FFinstance* instance, const char* data, size_t length) +{ + fputs(FASTFETCH_TEXT_MODIFIER_BOLT, stdout); + ffPrintCharTimes('\n', instance->config.logo.paddingTop); + ffPrintCharTimes(' ', instance->config.logo.paddingLeft); + fwrite(data, length, 1, stdout); + instance->state.logoHeight = instance->config.logo.paddingTop + instance->config.logo.height; + instance->state.logoWidth = instance->config.logo.paddingLeft + instance->config.logo.width + instance->config.logo.paddingRight; + printf("\033[9999999D\n\033[%uA", instance->state.logoHeight); +} + void ffLogoPrintChars(FFinstance* instance, const char* data, bool doColorReplacement) { uint32_t currentlineLength = 0; fputs(FASTFETCH_TEXT_MODIFIER_BOLT, stdout); + ffPrintCharTimes('\n', instance->config.logo.paddingTop); ffPrintCharTimes(' ', instance->config.logo.paddingLeft); + instance->state.logoHeight = instance->config.logo.paddingTop; + //Use logoColor[0] as the default color if(doColorReplacement) ffPrintColor(&instance->config.logo.colors[0]); @@ -191,7 +205,7 @@ static const FFlogo* logoGetBuiltin(const char* name) return NULL; } -static const FFlogo* logoGetBuiltinDetected(FFinstance* instance) +static const FFlogo* logoGetBuiltinDetected(const FFinstance* instance) { const FFOSResult* os = ffDetectOS(instance); @@ -211,7 +225,7 @@ static const FFlogo* logoGetBuiltinDetected(FFinstance* instance) if(logo != NULL) return logo; - logo = logoGetBuiltin(os->systemName.chars); + logo = logoGetBuiltin(instance->state.platform.systemName.chars); if(logo != NULL) return logo; @@ -271,19 +285,49 @@ static void logoPrintData(FFinstance* instance, bool doColorReplacement) { logoApplyColorsDetected(instance); } -static bool logoPrintFileIfExists(FFinstance* instance, bool doColorReplacement) +static bool loadLogoFile(const FFinstance* instance, FFstrbuf* buffer) +{ + if(ffAppendFileBuffer(instance->config.logo.source.chars, buffer)) + return true; + + FFstrbuf fullPath; + ffStrbufInit(&fullPath); + + FF_LIST_FOR_EACH(FFstrbuf, dataDir, instance->state.platform.dataDirs) + { + //We need to copy it, because multiple threads might be using dataDirs at the same time + ffStrbufSet(&fullPath, dataDir); + ffStrbufAppendS(&fullPath, "fastfetch/logos/"); + ffStrbufAppend(&fullPath, &instance->config.logo.source); + + if(ffAppendFileBuffer(fullPath.chars, buffer)) + { + ffStrbufDestroy(&fullPath); + return true; + } + } + + ffStrbufDestroy(&fullPath); + return false; +} + +static bool logoPrintFileIfExists(FFinstance* instance, bool doColorReplacement, bool raw) { FFstrbuf content; - ffStrbufInitA(&content, 2047); + ffStrbufInit(&content); - if(!ffAppendFileBuffer(instance->config.logo.source.chars, &content)) + if(!loadLogoFile(instance, &content)) { ffStrbufDestroy(&content); + fputs("Logo: Failed to load file content from logo source\n", stderr); return false; } logoApplyColorsDetected(instance); - ffLogoPrintChars(instance, content.chars, doColorReplacement); + if(raw) + ffLogoPrintCharsRaw(instance, content.chars, content.length); + else + ffLogoPrintChars(instance, content.chars, doColorReplacement); ffStrbufDestroy(&content); return true; } @@ -304,13 +348,25 @@ static void logoPrintKnownType(FFinstance* instance) 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); + successfull = logoPrintFileIfExists(instance, true, false); else if(instance->config.logo.type == FF_LOGO_TYPE_FILE_RAW) - successfull = logoPrintFileIfExists(instance, false); + successfull = logoPrintFileIfExists(instance, false, 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 if(instance->config.logo.type == FF_LOGO_TYPE_IMAGE_RAW) + { + if(instance->config.logo.width == 0 || instance->config.logo.height == 0) + { + fputs("both `--logo-width` and `--logo-height` must be specified\n", stderr); + successfull = false; + } + else + successfull = logoPrintFileIfExists(instance, false, true); + } + else if(instance->config.logo.type == FF_LOGO_TYPE_NONE) + logoApplyColorsDetected(instance); else //image successfull = logoPrintImageIfExists(instance, instance->config.logo.type); @@ -362,7 +418,7 @@ void ffLogoPrint(FFinstance* instance) return; //Try to load the logo as a file. If it succeeds, print it and return. - if(logoPrintFileIfExists(instance, true)) + if(logoPrintFileIfExists(instance, true, false)) return; logoPrintDetected(instance); diff --git a/src/modules/battery.c b/src/modules/battery.c index 97c603d9a..79cdf4c79 100644 --- a/src/modules/battery.c +++ b/src/modules/battery.c @@ -13,6 +13,7 @@ static void printBattery(FFinstance* instance, BatteryResult* result, uint8_t in ffPrintLogoAndKey(instance, FF_BATTERY_MODULE_NAME, index, &instance->config.battery.key); bool showStatus = + !(instance->config.percentType & FF_PERCENTAGE_TYPE_HIDE_OTHERS_BIT) && result->status.length > 0 && ffStrbufIgnCaseCompS(&result->status, "Unknown") != 0; @@ -35,7 +36,7 @@ static void printBattery(FFinstance* instance, BatteryResult* result, uint8_t in { if(str.length > 0) ffStrbufAppendC(&str, ' '); - ffStrbufAppendF(&str, "%.0f%%", result->capacity); + ffStrbufAppendF(&str, str.length > 0 ? "(%u%%)" : "%u%%", (unsigned) result->capacity); } } diff --git a/src/modules/brightness.c b/src/modules/brightness.c new file mode 100644 index 000000000..edede6543 --- /dev/null +++ b/src/modules/brightness.c @@ -0,0 +1,62 @@ +#include "fastfetch.h" +#include "common/printing.h" +#include "detection/brightness/brightness.h" + +#define FF_BRIGHTNESS_MODULE_NAME "Brightness" +#define FF_BRIGHTNESS_NUM_FORMAT_ARGS 2 + +void ffPrintBrightness(FFinstance* instance) +{ + FFlist result; + ffListInit(&result, sizeof(FFBrightnessResult)); + const char* error = ffDetectBrightness(&result); + + if(error) + { + ffPrintError(instance, FF_BRIGHTNESS_MODULE_NAME, 0, &instance->config.brightness, "%s", error); + goto exit; + } + + if(result.length == 0) + { + ffPrintError(instance, FF_BRIGHTNESS_MODULE_NAME, 0, &instance->config.brightness, "No result is detected."); + goto exit; + } + + FFstrbuf key; + ffStrbufInit(&key); + + FF_LIST_FOR_EACH(FFBrightnessResult, item, result) + { + if(instance->config.brightness.key.length == 0) + { + ffStrbufAppendF(&key, "%s (%s)", FF_BRIGHTNESS_MODULE_NAME, item->name.chars); + } + else + { + ffParseFormatString(&key, &instance->config.brightness.key, 1, (FFformatarg[]){ + {FF_FORMAT_ARG_TYPE_STRBUF, &item->name} + }); + } + + if(instance->config.brightness.outputFormat.length == 0) + { + ffPrintLogoAndKey(instance, key.chars, 0, NULL); + printf("%.0f%%\n", item->value); + } + else + { + ffPrintFormatString(instance, key.chars, 0, NULL, &instance->config.brightness.outputFormat, FF_BRIGHTNESS_NUM_FORMAT_ARGS, (FFformatarg[]) { + {FF_FORMAT_ARG_TYPE_STRBUF, &item->value}, + {FF_FORMAT_ARG_TYPE_FLOAT, &item->name} + }); + } + + ffStrbufDestroy(&item->name); + } + + ffStrbufDestroy(&key); + +exit: + ffListDestroy(&result); +} diff --git a/src/modules/command.c b/src/modules/command.c new file mode 100644 index 000000000..1b767013b --- /dev/null +++ b/src/modules/command.c @@ -0,0 +1,67 @@ +#include "fastfetch.h" + +#include "common/printing.h" +#include "common/processing.h" +#include "util/textModifier.h" + +#define FF_COMMAND_MODULE_NAME "Command" + +static void printError(FFinstance* instance, const char* key, const char* error) +{ + ffPrintLogoAndKey(instance, key, 0, NULL); + + if(instance->config.pipe) + fputs(FASTFETCH_TEXT_MODIFIER_ERROR, stdout); + + fputs(error, stdout); + + if(!instance->config.pipe) + fputs(FASTFETCH_TEXT_MODIFIER_RESET, stdout); + + putchar('\n'); +} + +void ffPrintCommand(FFinstance* instance) +{ + FF_STRBUF_AUTO_DESTROY key; + ffStrbufInit(&key); + + if(!ffListShift(&instance->config.commandKeys, &key)) + ffStrbufInitS(&key, FF_COMMAND_MODULE_NAME); + + FF_STRBUF_AUTO_DESTROY text; + ffStrbufInit(&text); + if(!ffListShift(&instance->config.commandTexts, &text)) + { + printError(instance, key.chars, "No command text left"); + return; + } + + FF_STRBUF_AUTO_DESTROY result; + ffStrbufInit(&result); + const char* error = ffProcessAppendStdOut(&result, (char* const[]){ + instance->config.commandShell.chars, + #ifdef _WIN32 + "/c", + #else + "-c", + #endif + text.chars, + NULL + }); + + if(error) + { + printError(instance, key.chars, error); + return; + } + + if(!result.length) + { + printError(instance, key.chars, "No result printed"); + return; + } + + ffPrintLogoAndKey(instance, key.chars, 0, NULL); + puts(result.chars); +} diff --git a/src/modules/disk.c b/src/modules/disk.c index eb6baeb47..c1e074812 100644 --- a/src/modules/disk.c +++ b/src/modules/disk.c @@ -47,17 +47,22 @@ static void printDisk(FFinstance* instance, const FFDisk* disk) ffAppendPercentBar(instance, &str, bytesPercentage, 0, 5, 8); ffStrbufAppendC(&str, ' '); } - ffStrbufAppendF(&str, "%s / %s", usedPretty.chars, totalPretty.chars); + + if(!(instance->config.percentType & FF_PERCENTAGE_TYPE_HIDE_OTHERS_BIT)) + ffStrbufAppendF(&str, "%s / %s ", usedPretty.chars, totalPretty.chars); if(instance->config.percentType & FF_PERCENTAGE_TYPE_NUM_BIT) - ffStrbufAppendF(&str, " (%u%%)", bytesPercentage); + { + ffStrbufAppendF(&str, str.length > 0 ? "(%u%%) " : "%u%% ", bytesPercentage); + } } else - ffStrbufAppendS(&str, "Unknown"); + ffStrbufAppendS(&str, "Unknown "); - if(disk->type == FF_DISK_TYPE_EXTERNAL) - ffStrbufAppendS(&str, " [Removable]"); + if(disk->type == FF_DISK_TYPE_EXTERNAL && !(instance->config.percentType & FF_PERCENTAGE_TYPE_HIDE_OTHERS_BIT)) + ffStrbufAppendS(&str, "[Removable]"); + ffStrbufTrimRight(&str, ' '); ffStrbufPutTo(&str, stdout); ffStrbufDestroy(&str); } @@ -132,9 +137,15 @@ static void printAutodetected(FFinstance* instance, const FFlist* disks) if(disk->type == FF_DISK_TYPE_EXTERNAL && !instance->config.diskShowRemovable) continue; + if(disk->type == FF_DISK_TYPE_SUBVOLUME && !instance->config.diskShowSubvolumes) + continue; + if(disk->type == FF_DISK_TYPE_HIDDEN && !instance->config.diskShowHidden) continue; + if(disk->bytesTotal == 0 && !instance->config.diskShowUnknown) + continue; + printDisk(instance, disk); } } diff --git a/src/modules/display.c b/src/modules/display.c new file mode 100644 index 000000000..45d93234b --- /dev/null +++ b/src/modules/display.c @@ -0,0 +1,51 @@ +#include "fastfetch.h" +#include "common/printing.h" +#include "detection/displayserver/displayserver.h" + +#define FF_RESOLUTION_MODULE_NAME "Display" +#define FF_RESOLUTION_NUM_FORMAT_ARGS 3 + +void ffPrintDisplay(FFinstance* instance) +{ + #ifdef __ANDROID__ + ffPrintError(instance, FF_RESOLUTION_MODULE_NAME, 0, &instance->config.display, "Display detection is not supported on Android"); + return; + #endif + + const FFDisplayServerResult* dsResult = ffConnectDisplayServer(instance); + + for(uint32_t i = 0; i < dsResult->displays.length; i++) + { + FFDisplayResult* result = ffListGet(&dsResult->displays, i); + uint8_t moduleIndex = dsResult->displays.length == 1 ? 0 : (uint8_t) (i + 1); + + if(instance->config.display.outputFormat.length == 0) + { + ffPrintLogoAndKey(instance, FF_RESOLUTION_MODULE_NAME, moduleIndex, &instance->config.display.key); + printf("%ix%i", result->width, result->height); + + if(result->refreshRate > 0) + printf(" @ %iHz", result->refreshRate); + + if( + result->scaledWidth > 0 && result->scaledWidth != result->width && + result->scaledHeight > 0 && result->scaledHeight != result->height) + printf(" (as %ix%i)", result->scaledWidth, result->scaledHeight); + + putchar('\n'); + } + else + { + ffPrintFormat(instance, FF_RESOLUTION_MODULE_NAME, moduleIndex, &instance->config.display, FF_RESOLUTION_NUM_FORMAT_ARGS, (FFformatarg[]) { + {FF_FORMAT_ARG_TYPE_UINT, &result->width}, + {FF_FORMAT_ARG_TYPE_UINT, &result->height}, + {FF_FORMAT_ARG_TYPE_UINT, &result->refreshRate}, + {FF_FORMAT_ARG_TYPE_UINT, &result->scaledWidth}, + {FF_FORMAT_ARG_TYPE_UINT, &result->scaledHeight} + }); + } + } + + if(dsResult->displays.length == 0) + ffPrintError(instance, FF_RESOLUTION_MODULE_NAME, 0, &instance->config.display, "Couldn't detect display"); +} diff --git a/src/modules/font.c b/src/modules/font.c index 25f394f61..0b2e05e16 100644 --- a/src/modules/font.c +++ b/src/modules/font.c @@ -48,12 +48,18 @@ 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) + if(i == 0 || !ffStrbufEqual(&font->fonts[i - 1], &font->fonts[i])) { - printf("%s [%s]", font->fonts[i].chars, types[i]); - break; + if(i > 0) + fputs("], ", stdout); + printf("%s [%s", font->fonts[i].chars, types[i]); + } + else + { + printf(" / %s", types[i]); } } + putchar(']'); } #else diff --git a/src/modules/gpu.c b/src/modules/gpu.c index 0d0ae8266..6c673df36 100644 --- a/src/modules/gpu.c +++ b/src/modules/gpu.c @@ -6,13 +6,13 @@ #include #define FF_GPU_MODULE_NAME "GPU" -#define FF_GPU_NUM_FORMAT_ARGS 5 +#define FF_GPU_NUM_FORMAT_ARGS 6 -static void printGPUResult(FFinstance* instance, uint8_t index, FFGPUResult* gpu) +static void printGPUResult(FFinstance* instance, uint8_t index, const FFGPUResult* gpu) { if(instance->config.gpu.outputFormat.length == 0) { - ffPrintLogoAndKey(instance, FF_GPU_MODULE_NAME, 0, &instance->config.gpu.key); + ffPrintLogoAndKey(instance, FF_GPU_MODULE_NAME, index, &instance->config.gpu.key); FFstrbuf output; ffStrbufInitA(&output, gpu->vendor.length + 1 + gpu->name.length); @@ -37,12 +37,21 @@ static void printGPUResult(FFinstance* instance, uint8_t index, FFGPUResult* gpu } else { + const char* type; + if(gpu->type == FF_GPU_TYPE_INTEGRATED) + type = "Integrated"; + else if(gpu->type == FF_GPU_TYPE_DISCRETE) + type = "Discrete"; + else + type = "Unknown"; + 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}, + {FF_FORMAT_ARG_TYPE_STRING, type}, }); } } @@ -51,12 +60,27 @@ void ffPrintGPU(FFinstance* instance) { const FFlist* gpus = ffDetectGPU(instance); - if(gpus->length == 0) + FFlist selectedGpus; + ffListInitA(&selectedGpus, sizeof(const FFGPUResult*), gpus->length); + + for(uint32_t i = 0; i < gpus->length; i++) { - ffPrintError(instance, FF_GPU_MODULE_NAME, 0, &instance->config.gpu, "No GPUs found"); - return; + const FFGPUResult* gpu = ffListGet(gpus, i); + + if(gpu->type == FF_GPU_TYPE_INTEGRATED && instance->config.gpuHideIntegrated) + continue; + + if(gpu->type == FF_GPU_TYPE_DISCRETE && instance->config.gpuHideDiscrete) + continue; + + * (const FFGPUResult**) ffListAdd(&selectedGpus) = gpu; } - for(uint8_t i = 0; i < (uint8_t) gpus->length; i++) - printGPUResult(instance, gpus->length == 1 ? 0 : (uint8_t) (i + 1), ffListGet(gpus, i)); + for(uint32_t i = 0; i < selectedGpus.length; i++) + printGPUResult(instance, selectedGpus.length == 1 ? 0 : (uint8_t) (i + 1), * (const FFGPUResult**) ffListGet(&selectedGpus, i)); + + if(selectedGpus.length == 0) + ffPrintError(instance, FF_GPU_MODULE_NAME, 0, &instance->config.gpu, "No GPUs found"); + + ffListDestroy(&selectedGpus); } diff --git a/src/modules/host.c b/src/modules/host.c index e8d8e6044..9fa3bafee 100644 --- a/src/modules/host.c +++ b/src/modules/host.c @@ -33,7 +33,7 @@ void ffPrintHost(FFinstance* instance) else ffStrbufAppend(&output, &host->productFamily); - if(host->productVersion.length > 0) + if(host->productVersion.length > 0 && !ffStrbufIgnCaseEqualS(&host->productVersion, "none")) { ffStrbufAppendF(&output, " (%s)", host->productVersion.chars); } diff --git a/src/modules/kernel.c b/src/modules/kernel.c index 72096ce55..bc1139833 100644 --- a/src/modules/kernel.c +++ b/src/modules/kernel.c @@ -2,29 +2,29 @@ #include "common/printing.h" #define FF_KERNEL_MODULE_NAME "Kernel" -#define FF_KERNEL_NUM_FORMAT_ARGS 3 +#define FF_KERNEL_NUM_FORMAT_ARGS 4 void ffPrintKernel(FFinstance* instance) { if(instance->config.kernel.outputFormat.length == 0) { ffPrintLogoAndKey(instance, FF_KERNEL_MODULE_NAME, 0, &instance->config.kernel.key); + ffStrbufWriteTo(&instance->state.platform.systemRelease, stdout); #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); + if(instance->state.platform.systemVersion.length > 0) + printf(" (%s)", instance->state.platform.systemVersion.chars); #endif + + putchar('\n'); } else { ffPrintFormat(instance, FF_KERNEL_MODULE_NAME, 0, &instance->config.kernel, FF_KERNEL_NUM_FORMAT_ARGS, (FFformatarg[]){ - {FF_FORMAT_ARG_TYPE_STRING, instance->state.utsname.sysname}, - {FF_FORMAT_ARG_TYPE_STRING, instance->state.utsname.release}, - {FF_FORMAT_ARG_TYPE_STRING, instance->state.utsname.version} + {FF_FORMAT_ARG_TYPE_STRBUF, &instance->state.platform.systemName}, + {FF_FORMAT_ARG_TYPE_STRBUF, &instance->state.platform.systemRelease}, + {FF_FORMAT_ARG_TYPE_STRBUF, &instance->state.platform.systemVersion}, + {FF_FORMAT_ARG_TYPE_STRBUF, &instance->state.platform.systemArchitecture} }); } } diff --git a/src/modules/memory.c b/src/modules/memory.c index 149eef45b..fbb893f02 100644 --- a/src/modules/memory.c +++ b/src/modules/memory.c @@ -52,11 +52,13 @@ static void printMemory(FFinstance* instance, const char* name, const FFModuleAr ffStrbufAppendC(&str, ' '); } - ffStrbufAppendF(&str, "%s / %s", usedPretty.chars, totalPretty.chars); + if(!(instance->config.percentType & FF_PERCENTAGE_TYPE_HIDE_OTHERS_BIT)) + ffStrbufAppendF(&str, "%s / %s ", usedPretty.chars, totalPretty.chars); if(instance->config.percentType & FF_PERCENTAGE_TYPE_NUM_BIT) - ffStrbufAppendF(&str, " (%u%%)", percentage); + ffStrbufAppendF(&str, str.length > 0 ? "(%u%%)" : "%u%%", percentage); + ffStrbufTrimRight(&str, ' '); ffStrbufPutTo(&str, stdout); ffStrbufDestroy(&str); } diff --git a/src/modules/os.c b/src/modules/os.c index 7d5a15842..140cfdbbe 100644 --- a/src/modules/os.c +++ b/src/modules/os.c @@ -7,7 +7,7 @@ #define FF_OS_MODULE_NAME "OS" #define FF_OS_NUM_FORMAT_ARGS 12 -static void buildOutputDefault(const FFOSResult* os, FFstrbuf* result) +static void buildOutputDefault(const FFinstance* instance, const FFOSResult* os, FFstrbuf* result) { //Create the basic output if(os->name.length > 0) @@ -17,7 +17,7 @@ static void buildOutputDefault(const FFOSResult* os, FFstrbuf* result) else if(os->id.length > 0) ffStrbufAppend(result, &os->id); else - ffStrbufAppend(result, &os->systemName); + ffStrbufAppend(result, &instance->state.platform.systemName); #ifdef __APPLE__ if(os->codename.length > 0) @@ -62,14 +62,14 @@ static void buildOutputDefault(const FFOSResult* os, FFstrbuf* result) } //Append architecture if it is missing - if(ffStrbufFirstIndex(result, &os->architecture) == result->length) + if(ffStrbufFirstIndex(result, &instance->state.platform.systemArchitecture) == result->length) { ffStrbufAppendC(result, ' '); - ffStrbufAppend(result, &os->architecture); + ffStrbufAppend(result, &instance->state.platform.systemArchitecture); } } -static void buildOutputNixOS(const FFOSResult* os, FFstrbuf* result) +static void buildOutputNixOS(const FFinstance* instance, const FFOSResult* os, FFstrbuf* result) { ffStrbufAppendS(result, "NixOS"); @@ -87,10 +87,10 @@ static void buildOutputNixOS(const FFOSResult* os, FFstrbuf* result) ffStrbufAppendC(result, ')'); } - if(os->architecture.length > 0) + if(instance->state.platform.systemArchitecture.length > 0) { ffStrbufAppendC(result, ' '); - ffStrbufAppend(result, &os->architecture); + ffStrbufAppend(result, &instance->state.platform.systemArchitecture); } } @@ -98,7 +98,7 @@ void ffPrintOS(FFinstance* instance) { const FFOSResult* os = ffDetectOS(instance); - if(os->name.length == 0 && os->prettyName.length == 0 && os->id.length == 0 && os->systemName.length == 0) + if(os->name.length == 0 && os->prettyName.length == 0 && os->id.length == 0) { ffPrintError(instance, FF_OS_MODULE_NAME, 0, &instance->config.os, "Could not detect OS"); return; @@ -110,9 +110,9 @@ void ffPrintOS(FFinstance* instance) ffStrbufInit(&result); if(ffStrbufIgnCaseCompS(&os->id, "nixos") == 0) - buildOutputNixOS(os, &result); + buildOutputNixOS(instance, os, &result); else - buildOutputDefault(os, &result); + buildOutputDefault(instance, os, &result); ffPrintLogoAndKey(instance, FF_OS_MODULE_NAME, 0, &instance->config.os.key); ffStrbufPutTo(&result, stdout); @@ -121,7 +121,7 @@ void ffPrintOS(FFinstance* instance) else { ffPrintFormat(instance, FF_OS_MODULE_NAME, 0, &instance->config.os, FF_OS_NUM_FORMAT_ARGS, (FFformatarg[]){ - {FF_FORMAT_ARG_TYPE_STRBUF, &os->systemName}, + {FF_FORMAT_ARG_TYPE_STRBUF, &instance->state.platform.systemName}, {FF_FORMAT_ARG_TYPE_STRBUF, &os->name}, {FF_FORMAT_ARG_TYPE_STRBUF, &os->prettyName}, {FF_FORMAT_ARG_TYPE_STRBUF, &os->id}, @@ -132,7 +132,7 @@ void ffPrintOS(FFinstance* instance) {FF_FORMAT_ARG_TYPE_STRBUF, &os->versionID}, {FF_FORMAT_ARG_TYPE_STRBUF, &os->codename}, {FF_FORMAT_ARG_TYPE_STRBUF, &os->buildID}, - {FF_FORMAT_ARG_TYPE_STRBUF, &os->architecture} + {FF_FORMAT_ARG_TYPE_STRBUF, &instance->state.platform.systemArchitecture} }); } } diff --git a/src/modules/resolution.c b/src/modules/resolution.c deleted file mode 100644 index 396d435b1..000000000 --- a/src/modules/resolution.c +++ /dev/null @@ -1,44 +0,0 @@ -#include "fastfetch.h" -#include "common/printing.h" -#include "detection/displayserver/displayserver.h" - -#define FF_RESOLUTION_MODULE_NAME "Resolution" -#define FF_RESOLUTION_NUM_FORMAT_ARGS 3 - -void ffPrintResolution(FFinstance* instance) -{ - #ifdef __ANDROID__ - ffPrintError(instance, FF_RESOLUTION_MODULE_NAME, 0, &instance->config.resolution, "Resolution detection is not supported on Android"); - return; - #endif - - const FFDisplayServerResult* dsResult = ffConnectDisplayServer(instance); - - for(uint32_t i = 0; i < dsResult->resolutions.length; i++) - { - FFResolutionResult* result = ffListGet(&dsResult->resolutions, i); - uint8_t moduleIndex = dsResult->resolutions.length == 1 ? 0 : (uint8_t) (i + 1); - - if(instance->config.resolution.outputFormat.length == 0) - { - ffPrintLogoAndKey(instance, FF_RESOLUTION_MODULE_NAME, moduleIndex, &instance->config.resolution.key); - printf("%ix%i", result->width, result->height); - - if(result->refreshRate > 0) - printf(" @ %iHz", result->refreshRate); - - putchar('\n'); - } - else - { - ffPrintFormat(instance, FF_RESOLUTION_MODULE_NAME, moduleIndex, &instance->config.resolution, FF_RESOLUTION_NUM_FORMAT_ARGS, (FFformatarg[]) { - {FF_FORMAT_ARG_TYPE_INT, &result->width}, - {FF_FORMAT_ARG_TYPE_INT, &result->height}, - {FF_FORMAT_ARG_TYPE_INT, &result->refreshRate} - }); - } - } - - if(dsResult->resolutions.length == 0) - ffPrintError(instance, FF_RESOLUTION_MODULE_NAME, 0, &instance->config.resolution, "Couldn't detect resolution"); -} diff --git a/src/modules/separator.c b/src/modules/separator.c index 7227f9ae4..879bc5bb8 100644 --- a/src/modules/separator.c +++ b/src/modules/separator.c @@ -1,12 +1,12 @@ #include "fastfetch.h" #include "common/printing.h" -#include "detection/title.h" void ffPrintSeparator(FFinstance* instance) { - const FFTitleResult* result = ffDetectTitle(instance); - uint32_t titleLength = result->userName.length + 1 + - (instance->config.titleFQDN ? result->fqdn.length : result->hostname.length); + uint32_t titleLength = instance->state.platform.userName.length + 1 + (instance->config.titleFQDN ? + instance->state.platform.domainName.length : + instance->state.platform.hostName.length + ); ffLogoPrintLine(instance); diff --git a/src/modules/terminal.c b/src/modules/terminal.c index 469ece23b..28e8d14a5 100644 --- a/src/modules/terminal.c +++ b/src/modules/terminal.c @@ -20,7 +20,11 @@ void ffPrintTerminal(FFinstance* instance) if(instance->config.terminal.outputFormat.length == 0) { ffPrintLogoAndKey(instance, FF_TERMINAL_MODULE_NAME, 0, &instance->config.terminal.key); - ffStrbufPutTo(&result->terminalPrettyName, stdout); + + if(result->terminalVersion.length) + printf("%s %s\n", result->terminalPrettyName.chars, result->terminalVersion.chars); + else + ffStrbufPutTo(&result->terminalPrettyName, stdout); } else { diff --git a/src/modules/title.c b/src/modules/title.c index cef00d146..4e509fbe5 100644 --- a/src/modules/title.c +++ b/src/modules/title.c @@ -1,6 +1,5 @@ #include "fastfetch.h" #include "common/printing.h" -#include "detection/title.h" #include "util/textModifier.h" static inline void printTitlePart(FFinstance* instance, const FFstrbuf* content) @@ -19,12 +18,13 @@ static inline void printTitlePart(FFinstance* instance, const FFstrbuf* content) void ffPrintTitle(FFinstance* instance) { - const FFTitleResult* result = ffDetectTitle(instance); - ffLogoPrintLine(instance); - printTitlePart(instance, &result->userName); + printTitlePart(instance, &instance->state.platform.userName); putchar('@'); - printTitlePart(instance, instance->config.titleFQDN ? &result->fqdn : &result->hostname); + printTitlePart(instance, instance->config.titleFQDN ? + &instance->state.platform.domainName : + &instance->state.platform.hostName + ); putchar('\n'); } diff --git a/src/modules/wifi.c b/src/modules/wifi.c index 596c18baa..fa9d19caf 100644 --- a/src/modules/wifi.c +++ b/src/modules/wifi.c @@ -3,67 +3,69 @@ #include "detection/wifi/wifi.h" #define FF_WIFI_MODULE_NAME "Wifi" -#define FF_WIFI_NUM_FORMAT_ARGS 12 +#define FF_WIFI_NUM_FORMAT_ARGS 10 void ffPrintWifi(FFinstance* instance) { - FFlist result; + FF_LIST_AUTO_DESTROY result; ffListInit(&result, sizeof(FFWifiResult)); const char* error = ffDetectWifi(instance, &result); - - if(!error) + 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); + ffPrintError(instance, FF_WIFI_MODULE_NAME, 0, &instance->config.wifi, "%s", error); + return; + } + if(!result.length) + { + ffPrintError(instance, FF_WIFI_MODULE_NAME, 0, &instance->config.wifi, "No Wifi interfaces found"); + return; + } - if(instance->config.wifi.outputFormat.length == 0) + 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) { - 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); - } + ffStrbufWriteTo(&item->conn.ssid, stdout); + if(item->conn.protocol.length) + printf("- %s", item->conn.protocol.chars); + if(item->conn.security.length) + printf("- %s", item->conn.security.chars); + putchar('\n'); } 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}, - }); + puts(item->inf.status.chars); } - - 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); + 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.protocol}, + {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_STRBUF, &item->conn.security}, + }); + } + + ffStrbufDestroy(&item->inf.description); + ffStrbufDestroy(&item->inf.status); + ffStrbufDestroy(&item->conn.status); + ffStrbufDestroy(&item->conn.ssid); + ffStrbufDestroy(&item->conn.macAddress); + ffStrbufDestroy(&item->conn.protocol); + ffStrbufDestroy(&item->conn.security); } } diff --git a/src/util/FFlist.c b/src/util/FFlist.c index d01353ffb..bd643014a 100644 --- a/src/util/FFlist.c +++ b/src/util/FFlist.c @@ -1,6 +1,7 @@ #include "FFlist.h" #include +#include void ffListInitA(FFlist* list, uint32_t elementSize, uint32_t capacity) { @@ -34,6 +35,27 @@ uint32_t ffListFirstIndexComp(const FFlist* list, void* compElement, bool(*compF return list->length; } +bool ffListShift(FFlist* list, void* result) +{ + if(list->length == 0) + return false; + + memcpy(result, list->data, list->elementSize); + memmove(list->data, list->data + list->elementSize, (size_t) list->elementSize * (list->length - 1)); + --list->length; + return true; +} + +bool ffListPop(FFlist* list, void* result) +{ + if(list->length == 0) + return false; + + memcpy(result, ffListGet(list, list->length - 1), list->elementSize); + --list->length; + return result; +} + void ffListDestroy(FFlist* list) { //Avoid free-after-use. These 3 assignments are cheap so don't remove them diff --git a/src/util/FFlist.h b/src/util/FFlist.h index fd7a0f5ca..58612e347 100644 --- a/src/util/FFlist.h +++ b/src/util/FFlist.h @@ -26,6 +26,11 @@ void* ffListAdd(FFlist* list); FF_C_NODISCARD uint32_t ffListFirstIndexComp(const FFlist* list, void* compElement, bool(*compFunc)(const void*, const void*)); +// Removes the first element, and copy its value to `*result` +bool ffListShift(FFlist* list, void* result); +// Removes the last element, and copy its value to `*result` +bool ffListPop(FFlist* list, void* result); + void ffListDestroy(FFlist* list); static inline void ffListInit(FFlist* list, uint32_t elementSize) @@ -45,8 +50,12 @@ 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 +#define FF_LIST_FOR_EACH(itemType, itemVarName, listVar) \ + assert(sizeof(itemType) == (listVar).elementSize); \ + for(itemType* itemVarName = (itemType*)(listVar).data; \ + itemVarName - (itemType*)(listVar).data < (intptr_t)(listVar).length; \ + ++itemVarName) + +#define FF_LIST_AUTO_DESTROY FFlist __attribute__((__cleanup__(ffListDestroy))) #endif diff --git a/src/util/FFstrbuf.c b/src/util/FFstrbuf.c index 27150ef24..375c53aaa 100644 --- a/src/util/FFstrbuf.c +++ b/src/util/FFstrbuf.c @@ -3,6 +3,17 @@ #include #include +bool ffStrSet(const char* str) +{ + if(str == NULL) + return false; + + while(isspace(*str)) + str++; + + return *str != '\0'; +} + static char* CHAR_NULL_PTR = ""; void ffStrbufInitA(FFstrbuf* strbuf, uint32_t allocate) @@ -328,6 +339,12 @@ uint32_t ffStrbufPreviousIndexC(const FFstrbuf* strbuf, uint32_t start, char c) return strbuf->length; } +void ffStrbufReplaceAllC(FFstrbuf* strbuf, char find, char replace) +{ + for (char *current_pos = strchr(strbuf->chars, find); current_pos; current_pos = strchr(current_pos + 1, find)) + *current_pos = replace; +} + void ffStrbufSubstrBefore(FFstrbuf* strbuf, uint32_t index) { if(strbuf->length <= index) diff --git a/src/util/FFstrbuf.h b/src/util/FFstrbuf.h index 22133a7ea..7342cb607 100644 --- a/src/util/FFstrbuf.h +++ b/src/util/FFstrbuf.h @@ -18,6 +18,8 @@ #define strcasestr StrStrIA #endif +bool ffStrSet(const char* str); + #define FASTFETCH_STRBUF_DEFAULT_ALLOC 32 typedef struct FFstrbuf @@ -69,6 +71,8 @@ FF_C_NODISCARD uint32_t ffStrbufNextIndexS(const FFstrbuf* strbuf, uint32_t star FF_C_NODISCARD uint32_t ffStrbufPreviousIndexC(const FFstrbuf* strbuf, uint32_t start, char c); +void ffStrbufReplaceAllC(FFstrbuf* strbuf, char find, char replace); + void ffStrbufSubstrBefore(FFstrbuf* strbuf, uint32_t index); void ffStrbufSubstrBeforeFirstC(FFstrbuf* strbuf, char c); void ffStrbufSubstrBeforeLastC(FFstrbuf* strbuf, char c); @@ -308,8 +312,6 @@ 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 +#define FF_STRBUF_AUTO_DESTROY FFstrbuf __attribute__((__cleanup__(ffStrbufDestroy))) #endif diff --git a/src/util/apple/cf_helpers.c b/src/util/apple/cf_helpers.c index 65dcd7248..f5c37b7ad 100644 --- a/src/util/apple/cf_helpers.c +++ b/src/util/apple/cf_helpers.c @@ -44,7 +44,10 @@ const char* ffCfDictGetString(CFDictionaryRef dict, CFStringRef key, FFstrbuf* r const char* ffCfDictGetBool(CFDictionaryRef dict, CFStringRef key, bool* result) { CFBooleanRef cf = (CFBooleanRef)CFDictionaryGetValue(dict, key); - if(cf == NULL || CFGetTypeID(cf) != CFBooleanGetTypeID()) + if(cf == NULL) + return "CFDictionaryGetValue() failed"; + + if(CFGetTypeID(cf) != CFBooleanGetTypeID()) return "TypeID is not 'CFBoolean'"; *result = CFBooleanGetValue(cf); @@ -53,13 +56,25 @@ const char* ffCfDictGetBool(CFDictionaryRef dict, CFStringRef key, bool* result) const char* ffCfDictGetInt(CFDictionaryRef dict, CFStringRef key, int* result) { - CFNumberRef cf = (CFNumberRef)CFDictionaryGetValue(dict, key); - if (cf == NULL || CFGetTypeID(cf) != CFNumberGetTypeID()) - return "TypeID is not 'CFNumber'"; + CFTypeRef cf = (CFTypeRef)CFDictionaryGetValue(dict, key); + if(cf == NULL) + return "CFDictionaryGetValue() failed"; - if(!CFNumberGetValue(cf, kCFNumberSInt32Type, result)) - return "Number type is not SInt32"; - return NULL; + if(CFGetTypeID(cf) == CFNumberGetTypeID()) + { + if(!CFNumberGetValue((CFNumberRef)cf, kCFNumberSInt32Type, result)) + return "Number type is not SInt32"; + return NULL; + } + else if(CFGetTypeID(cf) == CFDataGetTypeID()) + { + if(CFDataGetLength((CFDataRef)cf) != sizeof(int)) + return "Data length is not sizeof(int)"; + CFDataGetBytes((CFDataRef)cf, CFRangeMake(0, sizeof(int)), (uint8_t*)result); + return NULL; + } + + return "TypeID is neither 'CFNumber' nor 'CFData'"; } const char* ffCfDictGetDict(CFDictionaryRef dict, CFStringRef key, CFDictionaryRef* result) diff --git a/src/util/platform/FFPlatform.c b/src/util/platform/FFPlatform.c new file mode 100644 index 000000000..d549b6501 --- /dev/null +++ b/src/util/platform/FFPlatform.c @@ -0,0 +1,115 @@ +#include "FFPlatform_private.h" + +void ffPlatformInit(FFPlatform* platform) +{ + ffStrbufInit(&platform->homeDir); + ffStrbufInit(&platform->cacheDir); + ffListInit(&platform->configDirs, sizeof(FFstrbuf)); + ffListInit(&platform->dataDirs, sizeof(FFstrbuf)); + + ffStrbufInit(&platform->userName); + ffStrbufInit(&platform->hostName); + ffStrbufInit(&platform->domainName); + ffStrbufInit(&platform->userShell); + + ffStrbufInit(&platform->systemName); + ffStrbufInit(&platform->systemRelease); + ffStrbufInit(&platform->systemVersion); + ffStrbufInit(&platform->systemArchitecture); + + ffPlatformInitImpl(platform); + + if(platform->domainName.length == 0) + ffStrbufAppend(&platform->domainName, &platform->hostName); + + if(platform->systemName.length == 0) + { + #if defined(__linux__) + ffStrbufAppendS(&platform->systemName, "Linux"); + #elif defined(__FreeBSD__) + ffStrbufAppendS(&platform->systemName, "FreeBSD"); + #elif defined(__APPLE__) + ffStrbufAppendS(&platform->systemName, "Darwin"); + #elif defined(_WIN32) + ffStrbufAppendS(&platform->systemName, "Windows_NT"); + #else + ffStrbufAppendS(&platform->systemName, "Unknown"); + #endif + } + + if(platform->systemArchitecture.length == 0) + ffStrbufAppendS(&platform->systemArchitecture, "Unknown"); +} + +void ffPlatformDestroy(FFPlatform* platform) +{ + ffStrbufDestroy(&platform->homeDir); + ffStrbufDestroy(&platform->cacheDir); + + FF_LIST_FOR_EACH(FFstrbuf, dir, platform->configDirs) + ffStrbufDestroy(dir); + ffListDestroy(&platform->configDirs); + + FF_LIST_FOR_EACH(FFstrbuf, dir, platform->dataDirs) + ffStrbufDestroy(dir); + ffListDestroy(&platform->dataDirs); + + ffStrbufDestroy(&platform->userName); + ffStrbufDestroy(&platform->hostName); + ffStrbufDestroy(&platform->domainName); + ffStrbufDestroy(&platform->userShell); + + ffStrbufDestroy(&platform->systemArchitecture); + ffStrbufDestroy(&platform->systemName); + ffStrbufDestroy(&platform->systemRelease); + ffStrbufDestroy(&platform->systemVersion); +} + +void ffPlatformPathAddAbsolute(FFlist* dirs, const char* path) +{ + FFstrbuf* buffer = (FFstrbuf*) ffListAdd(dirs); + ffStrbufInitA(buffer, 64); + ffStrbufAppendS(buffer, path); + ffStrbufEnsureEndsWithC(buffer, '/'); + FF_PLATFORM_PATH_UNIQUE(dirs, buffer); +} + +void ffPlatformPathAddHome(FFlist* dirs, const FFPlatform* platform, const char* suffix) +{ + FFstrbuf* buffer = (FFstrbuf*) ffListAdd(dirs); + ffStrbufInitA(buffer, 64); + ffStrbufAppend(buffer, &platform->homeDir); + ffStrbufAppendS(buffer, suffix); + ffStrbufEnsureEndsWithC(buffer, '/'); + FF_PLATFORM_PATH_UNIQUE(dirs, buffer); +} + +void ffPlatformPathAddEnv(FFlist* dirs, const char* env) +{ + const char* envValue = getenv(env); + if(!ffStrSet(envValue)) + return; + + FFstrbuf value; + ffStrbufInitA(&value, 64); + ffStrbufAppendS(&value, envValue); + + uint32_t startIndex = 0; + while (startIndex < value.length) + { + uint32_t colonIndex = ffStrbufNextIndexC(&value, startIndex, ':'); + value.chars[colonIndex] = '\0'; + + if(!ffStrSet(value.chars + startIndex)) + { + startIndex = colonIndex + 1; + continue; + } + + ffPlatformPathAddAbsolute(dirs, value.chars + startIndex); + + startIndex = colonIndex + 1; + } + + ffStrbufDestroy(&value); +} diff --git a/src/util/platform/FFPlatform.h b/src/util/platform/FFPlatform.h new file mode 100644 index 000000000..caebc7c6f --- /dev/null +++ b/src/util/platform/FFPlatform.h @@ -0,0 +1,29 @@ +#pragma once + +#ifndef FF_INCLUDED_util_platform_FFPlatform +#define FF_INCLUDED_util_platform_FFPlatform + +#include "util/FFstrbuf.h" +#include "util/FFlist.h" + +typedef struct FFPlatform { + FFstrbuf homeDir; // Trailing slash included + FFstrbuf cacheDir; // Trailing slash included + FFlist configDirs; // List of FFstrbuf, trailing slash included + FFlist dataDirs; // List of FFstrbuf, trailing slash included + + FFstrbuf userName; + FFstrbuf hostName; + FFstrbuf domainName; + FFstrbuf userShell; + + FFstrbuf systemName; + FFstrbuf systemRelease; + FFstrbuf systemVersion; + FFstrbuf systemArchitecture; +} FFPlatform; + +void ffPlatformInit(FFPlatform* platform); +void ffPlatformDestroy(FFPlatform* platform); + +#endif diff --git a/src/util/platform/FFPlatform_private.h b/src/util/platform/FFPlatform_private.h new file mode 100644 index 000000000..698f07609 --- /dev/null +++ b/src/util/platform/FFPlatform_private.h @@ -0,0 +1,21 @@ +#pragma once + +#ifndef FF_INCLUDED_util_platform_FFPlatform_private +#define FF_INCLUDED_util_platform_FFPlatform_private + +#include "FFPlatform.h" + +void ffPlatformInitImpl(FFPlatform* platform); + +#define FF_PLATFORM_PATH_UNIQUE(list, element) \ + if(ffListFirstIndexComp(list, element, (bool(*)(const void*, const void*))ffStrbufEqual) < list->length - 1) \ + { \ + ffStrbufDestroy(ffListGet(list, list->length - 1)); \ + --list->length; \ + } + +void ffPlatformPathAddAbsolute(FFlist* dirs, const char* path); +void ffPlatformPathAddHome(FFlist* dirs, const FFPlatform* platform, const char* suffix); +void ffPlatformPathAddEnv(FFlist* dirs, const char* env); + +#endif diff --git a/src/util/platform/FFPlatform_unix.c b/src/util/platform/FFPlatform_unix.c new file mode 100644 index 000000000..393d2290b --- /dev/null +++ b/src/util/platform/FFPlatform_unix.c @@ -0,0 +1,149 @@ +#include "FFPlatform_private.h" + +#include "fastfetch_config.h" + +#include +#include +#include + +static void getHomeDir(FFPlatform* platform, const struct passwd* pwd) +{ + const char* home = getenv("HOME"); + if(!ffStrSet(home) && pwd) + home = pwd->pw_dir; + + ffStrbufAppendS(&platform->homeDir, home); + ffStrbufEnsureEndsWithC(&platform->homeDir, '/'); +} + +static void getCacheDir(FFPlatform* platform) +{ + const char* cache = getenv("XDG_CACHE_HOME"); + if(ffStrSet(cache)) + { + ffStrbufAppendS(&platform->cacheDir, cache); + ffStrbufEnsureEndsWithC(&platform->cacheDir, '/'); + } + else + { + ffStrbufAppend(&platform->cacheDir, &platform->homeDir); + ffStrbufAppendS(&platform->cacheDir, ".cache/"); + } + + ffStrbufAppendS(&platform->cacheDir, "fastfetch/"); +} + +static void getConfigDirs(FFPlatform* platform) +{ + ffPlatformPathAddEnv(&platform->configDirs, "XDG_CONFIG_HOME"); + ffPlatformPathAddHome(&platform->configDirs, platform, ".config/"); + + #if defined(__APPLE__) + ffPlatformPathAddHome(&platform->configDirs, platform, "Library/Preferences/"); + #endif + + ffPlatformPathAddHome(&platform->configDirs, platform, ""); + ffPlatformPathAddEnv(&platform->configDirs, "XDG_CONFIG_DIRS"); + + #if !defined(__APPLE__) + ffPlatformPathAddAbsolute(&platform->configDirs, FASTFETCH_TARGET_DIR_ETC"/xdg/"); + #endif + + ffPlatformPathAddAbsolute(&platform->configDirs, FASTFETCH_TARGET_DIR_ETC"/"); + ffPlatformPathAddAbsolute(&platform->configDirs, FASTFETCH_TARGET_DIR_INSTALL_SYSCONF"/"); +} + +static void getDataDirs(FFPlatform* platform) +{ + ffPlatformPathAddEnv(&platform->dataDirs, "XDG_DATA_HOME"); + ffPlatformPathAddHome(&platform->dataDirs, platform, ".local/share/"); + + #if defined(__APPLE__) + ffPlatformPathAddHome(&platform->dataDirs, platform, "/Library/Application Support/"); + #endif + + ffPlatformPathAddHome(&platform->dataDirs, platform, ""); + ffPlatformPathAddEnv(&platform->dataDirs, "XDG_DATA_DIRS"); + ffPlatformPathAddAbsolute(&platform->dataDirs, FASTFETCH_TARGET_DIR_USR"/local/share/"); + ffPlatformPathAddAbsolute(&platform->dataDirs, FASTFETCH_TARGET_DIR_USR"/share/"); +} + +static void getUserName(FFPlatform* platform, const struct passwd* pwd) +{ + const char* user = getenv("USER"); + if(!ffStrSet(user) && pwd) + user = pwd->pw_name; + + ffStrbufAppendS(&platform->userName, user); +} + +static void getHostName(FFPlatform* platform, const struct utsname* uts) +{ + char hostname[256]; + if(gethostname(hostname, sizeof(hostname)) == 0) + ffStrbufAppendS(&platform->hostName, hostname); + + if(platform->hostName.length == 0) + ffStrbufAppendS(&platform->hostName, uts->nodename); +} + +#ifdef __linux__ +#include +static void getDomainName(FFPlatform* platform) +{ + struct addrinfo hints = {0}; + hints.ai_socktype = SOCK_STREAM; + hints.ai_flags = AI_CANONNAME; + + struct addrinfo* info = NULL; + + if(getaddrinfo(platform->hostName.chars, "80", &hints, &info) != 0) + return; + + struct addrinfo* current = info; + while(platform->domainName.length == 0 && current != NULL) + { + ffStrbufAppendS(&platform->domainName, current->ai_canonname); + current = current->ai_next; + } + + freeaddrinfo(info); +} +#endif + +static void getUserShell(FFPlatform* platform, const struct passwd* pwd) +{ + const char* shell = getenv("SHELL"); + if(!ffStrSet(shell) && pwd) + shell = pwd->pw_shell; + + ffStrbufAppendS(&platform->userShell, shell); +} + +void ffPlatformInitImpl(FFPlatform* platform) +{ + struct passwd* pwd = getpwuid(getuid()); + + struct utsname uts; + if(uname(&uts) != 0) + memset(&uts, 0, sizeof(uts)); + + getHomeDir(platform, pwd); + getCacheDir(platform); + getConfigDirs(platform); + getDataDirs(platform); + + getUserName(platform, pwd); + getHostName(platform, &uts); + + #ifdef __linux__ + getDomainName(platform); + #endif + + getUserShell(platform, pwd); + + ffStrbufAppendS(&platform->systemName, uts.sysname); + ffStrbufAppendS(&platform->systemRelease, uts.release); + ffStrbufAppendS(&platform->systemVersion, uts.version); + ffStrbufAppendS(&platform->systemArchitecture, uts.machine); +} diff --git a/src/util/platform/FFPlatform_windows.c b/src/util/platform/FFPlatform_windows.c new file mode 100644 index 000000000..2fff50ef5 --- /dev/null +++ b/src/util/platform/FFPlatform_windows.c @@ -0,0 +1,232 @@ +#include "FFPlatform_private.h" + +#define WIN32_LEAN_AND_MEAN +#include +#include + +#include "util/windows/unicode.h" + +static void getHomeDir(FFPlatform* platform) +{ + PWSTR pPath; + if(SUCCEEDED(SHGetKnownFolderPath(&FOLDERID_Profile, KF_FLAG_DEFAULT, NULL, &pPath))) + { + ffStrbufSetWS(&platform->homeDir, pPath); + ffStrbufReplaceAllC(&platform->homeDir, '\\', '/'); + ffStrbufEnsureEndsWithC(&platform->homeDir, '/'); + } + CoTaskMemFree(pPath); +} + +static void getCacheDir(FFPlatform* platform) +{ + PWSTR pPath; + if(SUCCEEDED(SHGetKnownFolderPath(&FOLDERID_LocalAppData, KF_FLAG_DEFAULT, NULL, &pPath))) + { + ffStrbufSetWS(&platform->cacheDir, pPath); + ffStrbufReplaceAllC(&platform->cacheDir, '\\', '/'); + ffStrbufEnsureEndsWithC(&platform->cacheDir, '/'); + } + else + { + ffStrbufAppend(&platform->cacheDir, &platform->homeDir); + ffStrbufAppendS(&platform->cacheDir, "AppData/Local/"); + } + CoTaskMemFree(pPath); + + ffStrbufAppendS(&platform->cacheDir, "fastfetch/"); +} + +static void platformPathAddKnownFolder(FFlist* dirs, REFKNOWNFOLDERID folderId) +{ + PWSTR pPath; + if(SUCCEEDED(SHGetKnownFolderPath(folderId, 0, NULL, &pPath))) + { + FFstrbuf* buffer = (FFstrbuf*) ffListAdd(dirs); + ffStrbufInit(buffer); + ffStrbufSetWS(buffer, pPath); + ffStrbufReplaceAllC(buffer, '\\', '/'); + ffStrbufEnsureEndsWithC(buffer, '/'); + FF_PLATFORM_PATH_UNIQUE(dirs, buffer); + } + CoTaskMemFree(pPath); +} + +static void platformPathAddEnvSuffix(FFlist* dirs, const char* env, const char* suffix) +{ + const char* value = getenv(env); + if(!ffStrSet(value)) + return; + + FFstrbuf* buffer = ffListAdd(dirs); + ffStrbufInitA(buffer, 64); + ffStrbufAppendS(buffer, value); + ffStrbufReplaceAllC(buffer, '\\', '/'); + ffStrbufEnsureEndsWithC(buffer, '/'); + ffStrbufAppendS(buffer, suffix); + ffStrbufEnsureEndsWithC(buffer, '/'); + FF_PLATFORM_PATH_UNIQUE(dirs, buffer); +} + +static void getConfigDirs(FFPlatform* platform) +{ + ffListInit(&platform->configDirs, sizeof(FFstrbuf)); + + ffPlatformPathAddEnv(&platform->configDirs, "XDG_CONFIG_HOME"); + ffPlatformPathAddHome(&platform->configDirs, platform, "/.config/"); + platformPathAddKnownFolder(&platform->configDirs, &FOLDERID_RoamingAppData); + platformPathAddKnownFolder(&platform->configDirs, &FOLDERID_LocalAppData); + ffPlatformPathAddHome(&platform->configDirs, platform, ""); + + if(getenv("MSYSTEM") && getenv("HOME")) + { + // We are in MSYS2 / Git Bash + platformPathAddEnvSuffix(&platform->configDirs, "HOME", ".config/"); + platformPathAddEnvSuffix(&platform->configDirs, "HOME", ""); + } + + ffPlatformPathAddEnv(&platform->configDirs, "XDG_CONFIG_DIRS"); +} + +static void getDataDirs(FFPlatform* platform) +{ + ffPlatformPathAddEnv(&platform->dataDirs, "XDG_DATA_HOME"); + ffPlatformPathAddHome(&platform->dataDirs, platform, "/.local/share/"); + platformPathAddKnownFolder(&platform->dataDirs, &FOLDERID_RoamingAppData); + platformPathAddKnownFolder(&platform->dataDirs, &FOLDERID_LocalAppData); + ffPlatformPathAddHome(&platform->dataDirs, platform, ""); + + if(getenv("MSYSTEM") && getenv("HOME")) + { + // We are in MSYS2 / Git Bash + platformPathAddEnvSuffix(&platform->dataDirs, "HOME", ".local/share/"); + platformPathAddEnvSuffix(&platform->dataDirs, "HOME", ""); + } + + ffPlatformPathAddEnv(&platform->dataDirs, "XDG_DATA_DIRS"); +} + +static void getUserName(FFPlatform* platform) +{ + ffStrbufEnsureFree(&platform->userName, 64); + DWORD len = (DWORD) ffStrbufGetFree(&platform->userName); + if(GetUserNameA(platform->userName.chars, &len)) + platform->userName.length = (uint32_t) len; +} + +static void getHostName(FFPlatform* platform) +{ + ffStrbufEnsureFree(&platform->hostName, 64); + DWORD len = (DWORD) ffStrbufGetFree(&platform->hostName); + if(GetComputerNameExA(ComputerNameDnsHostname, platform->hostName.chars, &len)) + platform->hostName.length = (uint32_t) len; +} + +static void getDomainName(FFPlatform* platform) +{ + ffStrbufEnsureFree(&platform->domainName, 64); + DWORD len = (DWORD) ffStrbufGetFree(&platform->domainName); + if(GetComputerNameExA(ComputerNameDnsDomain, platform->domainName.chars, &len)) + platform->domainName.length = (uint32_t) len; +} + +static void getSystemName(FFPlatform* platform) +{ + ffStrbufAppendS(&platform->systemName, "Windows_NT"); +} + +static void getSystemReleaseAndVersion(FFPlatform* platform) +{ + HKEY hKey; + if(RegOpenKeyExW(HKEY_LOCAL_MACHINE, L"SOFTWARE\\Microsoft\\Windows NT\\CurrentVersion", 0, KEY_QUERY_VALUE, &hKey) != ERROR_SUCCESS) + return; + + 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; + + ffStrbufAppendF(&platform->systemRelease, "%s.%s.%u", currentVersion, currentBuildNumber, (unsigned)ubr); + + ffStrbufEnsureFree(&platform->systemVersion, 256); + bufSize = (DWORD) ffStrbufGetFree(&platform->systemVersion); + if(RegGetValueA(hKey, NULL, "DisplayVersion", RRF_RT_REG_SZ, NULL, platform->systemVersion.chars, &bufSize) == ERROR_SUCCESS) + platform->systemVersion.length = (uint32_t) bufSize; + + RegCloseKey(hKey); +} + +static void getSystemArchitecture(FFPlatform* platform) +{ + SYSTEM_INFO sysInfo = {0}; + GetNativeSystemInfo(&sysInfo); + + switch(sysInfo.wProcessorArchitecture) + { + case PROCESSOR_ARCHITECTURE_AMD64: + ffStrbufAppendS(&platform->systemArchitecture, "x86_64"); + break; + case PROCESSOR_ARCHITECTURE_IA64: + ffStrbufAppendS(&platform->systemArchitecture, "ia64"); + break; + case PROCESSOR_ARCHITECTURE_INTEL: + ffStrbufAppendS(&platform->systemArchitecture, "i686"); + break; + case PROCESSOR_ARCHITECTURE_ARM64: + ffStrbufAppendS(&platform->systemArchitecture, "aarch64"); + break; + case PROCESSOR_ARCHITECTURE_ARM: + ffStrbufAppendS(&platform->systemArchitecture, "arm"); + break; + case PROCESSOR_ARCHITECTURE_PPC: + ffStrbufAppendS(&platform->systemArchitecture, "ppc"); + break; + case PROCESSOR_ARCHITECTURE_MIPS: + ffStrbufAppendS(&platform->systemArchitecture, "mips"); + break; + case PROCESSOR_ARCHITECTURE_UNKNOWN: + default: + break; + } +} + +void ffPlatformInitImpl(FFPlatform* platform) +{ + getHomeDir(platform); + getCacheDir(platform); + getConfigDirs(platform); + getDataDirs(platform); + + getUserName(platform); + getHostName(platform); + getDomainName(platform); + + getSystemName(platform); + getSystemReleaseAndVersion(platform); + getSystemArchitecture(platform); +} diff --git a/src/util/windows/manifest.xml b/src/util/windows/manifest.xml index 420545ded..86d623144 100644 --- a/src/util/windows/manifest.xml +++ b/src/util/windows/manifest.xml @@ -16,7 +16,7 @@ UTF-8 - true + false SegmentHeap diff --git a/src/util/windows/pwd.c b/src/util/windows/pwd.c deleted file mode 100644 index a52f2689a..000000000 --- a/src/util/windows/pwd.c +++ /dev/null @@ -1,17 +0,0 @@ -#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 deleted file mode 100644 index 29d4c883b..000000000 --- a/src/util/windows/pwd.h +++ /dev/null @@ -1,22 +0,0 @@ -#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/unicode.h b/src/util/windows/unicode.h index d70fbb22e..f01080b0a 100644 --- a/src/util/windows/unicode.h +++ b/src/util/windows/unicode.h @@ -3,16 +3,18 @@ #ifndef FASTFETCH_INCLUDED_UNICODE_H #define FASTFETCH_INCLUDED_UNICODE_H -#include "fastfetch.h" +#include "util/FFstrbuf.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); diff --git a/src/util/windows/utsname.c b/src/util/windows/utsname.c deleted file mode 100644 index a1f68021d..000000000 --- a/src/util/windows/utsname.c +++ /dev/null @@ -1,116 +0,0 @@ -#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 deleted file mode 100644 index 96bc53adc..000000000 --- a/src/util/windows/utsname.h +++ /dev/null @@ -1,19 +0,0 @@ -#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/wmi.cpp b/src/util/windows/wmi.cpp index cba0c07c5..1da4ac0ff 100644 --- a/src/util/windows/wmi.cpp +++ b/src/util/windows/wmi.cpp @@ -26,7 +26,7 @@ static void CoUninitializeWrap() CoUninitialize(); } -static BOOL CALLBACK InitHandleFunction(PINIT_ONCE, PVOID, PVOID *lpContext) +static BOOL CALLBACK InitHandleFunction(PINIT_ONCE, PVOID lpParameter, PVOID* lpContext) { HRESULT hres; @@ -76,15 +76,15 @@ static BOOL CALLBACK InitHandleFunction(PINIT_ONCE, PVOID, PVOID *lpContext) // 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 - ); + bstr_t((const wchar_t*) lpParameter), // 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; @@ -120,13 +120,17 @@ static BOOL CALLBACK InitHandleFunction(PINIT_ONCE, PVOID, PVOID *lpContext) return TRUE; } -FFWmiQuery::FFWmiQuery(const wchar_t* queryStr, FFstrbuf* error) +FFWmiQuery::FFWmiQuery(const wchar_t* queryStr, FFstrbuf* error, FFWmiNamespace wmiNs) : pEnumerator(nullptr) { - static INIT_ONCE s_InitOnce = INIT_ONCE_STATIC_INIT; + static INIT_ONCE s_InitOnce[(int) FFWmiNamespace::LAST] = {}; const char* context; - if (InitOnceExecuteOnce(&s_InitOnce, &InitHandleFunction, nullptr, (void**)&context) == FALSE) - { + if (InitOnceExecuteOnce( + &s_InitOnce[(int)wmiNs], + &InitHandleFunction, + (PVOID) (wmiNs == FFWmiNamespace::CIMV2 ? L"ROOT\\CIMV2" : L"ROOT\\WMI"), + (void**)&context) == FALSE + ) { if(error) ffStrbufAppendS(error, context); return; diff --git a/src/util/windows/wmi.hpp b/src/util/windows/wmi.hpp index 6f7d214cb..25bef6e91 100644 --- a/src/util/windows/wmi.hpp +++ b/src/util/windows/wmi.hpp @@ -11,6 +11,12 @@ extern "C" { #include +enum class FFWmiNamespace { + CIMV2, + WMI, + LAST, +}; + struct FFWmiRecord { IWbemClassObject* obj; @@ -43,7 +49,7 @@ struct FFWmiQuery { IEnumWbemClassObject* pEnumerator = nullptr; - FFWmiQuery(const wchar_t* queryStr, FFstrbuf* error = nullptr); + FFWmiQuery(const wchar_t* queryStr, FFstrbuf* error = nullptr, FFWmiNamespace wmiNs = FFWmiNamespace::CIMV2); explicit FFWmiQuery(IEnumWbemClassObject* pEnumerator): pEnumerator(pEnumerator) {} FFWmiQuery(const FFWmiQuery& other) = delete; FFWmiQuery(FFWmiQuery&& other) { *this = (FFWmiQuery&&)other; } diff --git a/tests/list.c b/tests/list.c index e9c10423e..db4db544a 100644 --- a/tests/list.c +++ b/tests/list.c @@ -31,6 +31,7 @@ static bool numEqualsAdapter(const void* first, const void* second) int main(void) { FFlist list; + uint32_t n = 0; //initA @@ -40,6 +41,18 @@ int main(void) VERIFY(list.capacity == 0); VERIFY(list.length == 0); + //forEach + FF_LIST_FOR_EACH(uint32_t, item, list) + VERIFY(false); + + //shift + VERIFY(!ffListShift(&list, &n)); + VERIFY(list.length == 0); + + //pop + VERIFY(!ffListPop(&list, &n)); + VERIFY(list.length == 0); + //add for (uint32_t i = 1; i <= FF_LIST_DEFAULT_ALLOC + 1; ++i) { @@ -60,13 +73,34 @@ int main(void) VERIFY(*(uint32_t*)ffListGet(&list, 0) == 1); VERIFY(*(uint32_t*)ffListGet(&list, i - 1) == i); } + VERIFY(list.length == FF_LIST_DEFAULT_ALLOC + 1); + + uint32_t sum = 0; + //forEach + FF_LIST_FOR_EACH(uint32_t, item, list) + sum += *item; + VERIFY(sum == (1 + FF_LIST_DEFAULT_ALLOC + 1) * (FF_LIST_DEFAULT_ALLOC + 1) / 2); // ffListFirstIndexComp - uint32_t n = 10; + n = 10; VERIFY(ffListFirstIndexComp(&list, &n, numEqualsAdapter) == 9); n = 999; VERIFY(ffListFirstIndexComp(&list, &n, numEqualsAdapter) == list.length); + //shift + VERIFY(ffListShift(&list, &n)); + VERIFY(n == 1); + VERIFY(list.length == FF_LIST_DEFAULT_ALLOC); + VERIFY(*(uint32_t*) ffListGet(&list, 0) == 2); + VERIFY(*(uint32_t*) ffListGet(&list, list.length - 1) == FF_LIST_DEFAULT_ALLOC + 1); + + //pop + VERIFY(ffListPop(&list, &n)); + VERIFY(n == FF_LIST_DEFAULT_ALLOC + 1); + VERIFY(list.length == FF_LIST_DEFAULT_ALLOC - 1); + VERIFY(*(uint32_t*) ffListGet(&list, 0) == 2); + VERIFY(*(uint32_t*) ffListGet(&list, list.length - 1) == FF_LIST_DEFAULT_ALLOC); + //Destroy ffListDestroy(&list); diff --git a/tests/strbuf.c b/tests/strbuf.c index 3b55142ec..3d8d3d057 100644 --- a/tests/strbuf.c +++ b/tests/strbuf.c @@ -208,6 +208,12 @@ int main(void) VERIFY(ffStrbufContainC(&strbuf, '1')); VERIFY(!ffStrbufContainC(&strbuf, '-')); + //replaceAllC + ffStrbufReplaceAllC(&strbuf, '1', '-'); + VERIFY(ffStrbufEqualS(&strbuf, "-234567890-234567890-234567890-")); + ffStrbufReplaceAllC(&strbuf, '1', '-'); + VERIFY(ffStrbufEqualS(&strbuf, "-234567890-234567890-234567890-")); + ffStrbufDestroy(&strbuf); //Success