diff --git a/.codespellrc b/.codespellrc index 0dce5c4692..087150a6d5 100644 --- a/.codespellrc +++ b/.codespellrc @@ -2,4 +2,4 @@ check-filenames = builtin = clear,rare,usage,informal skip = */.git,*/cmake-build-*,*/.idea,*/completions,*/presets,*/screenshots,*/tests,*/3rdparty,*/logo/ascii -ignore-words-list = iterm,compiletime,unknwn,pengwin,siduction,master,sur,doas +ignore-words-list = iterm,compiletime,unknwn,pengwin,siduction,master,sur,doas,conexant diff --git a/.github/ISSUE_TEMPLATE/logo_request.md b/.github/ISSUE_TEMPLATE/logo_request.md index 98bfff8988..ea141df00e 100644 --- a/.github/ISSUE_TEMPLATE/logo_request.md +++ b/.github/ISSUE_TEMPLATE/logo_request.md @@ -15,6 +15,9 @@ Paste content of /etc/os-release and /etc/lsb-release here. If none of these fil ``` # Ascii + +An ASCII logo should not take up too much space (smaller than 50x20 characters). Please also include the color codes if not available in `os-release` + ``` Paste ascii art here. ``` diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 2e8e234140..b5fe55b6c8 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -35,7 +35,7 @@ jobs: run: uname -a - name: configure project - run: cmake -DSET_TWEAK=Off -DBUILD_TESTS=On -DCMAKE_INSTALL_PREFIX=/usr . -DENABLE_VULKAN=OFF -DENABLE_WAYLAND=OFF -DENABLE_XCB_RANDR=OFF -DENABLE_XCB=OFF -DENABLE_XRANDR=OFF -DENABLE_X11=OFF -DENABLE_DRM=OFF -DENABLE_GIO=OFF -DENABLE_DCONF=OFF -DENABLE_DBUS=OFF -DENABLE_XFCONF=OFF -DENABLE_SQLITE3=OFF -DENABLE_RPM=OFF -DENABLE_IMAGEMAGICK7=OFF -DENABLE_IMAGEMAGICK6=OFF -DENABLE_CHAFA=OFF -DENABLE_ZLIB=OFF -DENABLE_EGL=OFF -DENABLE_GLX=OFF -DENABLE_OSMESA=OFF -DENABLE_OPENCL=OFF -DENABLE_LIBNM=OFF -DENABLE_FREETYPE=OFF -DENABLE_PULSE=OFF -DENABLE_DDCUTIL=OFF -DENABLE_DIRECTX_HEADERS=OFF -DENABLE_THREADS=OFF + run: cmake -DSET_TWEAK=Off -DBUILD_TESTS=On -DCMAKE_INSTALL_PREFIX=/usr . -DENABLE_VULKAN=OFF -DENABLE_WAYLAND=OFF -DENABLE_XCB_RANDR=OFF -DENABLE_XCB=OFF -DENABLE_XRANDR=OFF -DENABLE_X11=OFF -DENABLE_DRM=OFF -DENABLE_GIO=OFF -DENABLE_DCONF=OFF -DENABLE_DBUS=OFF -DENABLE_XFCONF=OFF -DENABLE_SQLITE3=OFF -DENABLE_RPM=OFF -DENABLE_IMAGEMAGICK7=OFF -DENABLE_IMAGEMAGICK6=OFF -DENABLE_CHAFA=OFF -DENABLE_ZLIB=OFF -DENABLE_EGL=OFF -DENABLE_GLX=OFF -DENABLE_OSMESA=OFF -DENABLE_OPENCL=OFF -DENABLE_FREETYPE=OFF -DENABLE_PULSE=OFF -DENABLE_DDCUTIL=OFF -DENABLE_DIRECTX_HEADERS=OFF -DENABLE_THREADS=OFF - name: build project run: cmake --build . --target package --verbose -j4 @@ -74,7 +74,7 @@ jobs: run: uname -a - name: install required packages - run: sudo apt-get update && sudo apt-get install -y libvulkan-dev libwayland-dev libxrandr-dev libxcb-randr0-dev libdconf-dev libdbus-1-dev libmagickcore-dev libxfconf-0-dev libsqlite3-dev librpm-dev libegl-dev libglx-dev libosmesa6-dev ocl-icd-opencl-dev libnm-dev libpulse-dev libdrm-dev directx-headers-dev + run: sudo apt-get update && sudo apt-get install -y libvulkan-dev libwayland-dev libxrandr-dev libxcb-randr0-dev libdconf-dev libdbus-1-dev libmagickcore-dev libxfconf-0-dev libsqlite3-dev librpm-dev libegl-dev libglx-dev libosmesa6-dev ocl-icd-opencl-dev libpulse-dev libdrm-dev directx-headers-dev - name: install linuxbrew packages run: | @@ -138,11 +138,11 @@ jobs: id: runcmd with: arch: aarch64 - distro: ubuntu22.04 + distro: ubuntu20.04 githubToken: ${{ github.token }} run: | uname -a - apt-get update && apt-get install -y cmake make g++ libvulkan-dev libwayland-dev libxrandr-dev libxcb-randr0-dev libdconf-dev libdbus-1-dev libmagickcore-dev libxfconf-0-dev libsqlite3-dev librpm-dev libegl-dev libglx-dev libosmesa6-dev ocl-icd-opencl-dev libnm-dev libpulse-dev libdrm-dev libddcutil-dev libchafa-dev directx-headers-dev rpm + apt-get update && apt-get install -y cmake make g++ libvulkan-dev libwayland-dev libxrandr-dev libxcb-randr0-dev libdconf-dev libdbus-1-dev libmagickcore-dev libxfconf-0-dev libsqlite3-dev librpm-dev libegl-dev libglx-dev libosmesa6-dev ocl-icd-opencl-dev libpulse-dev libdrm-dev directx-headers-dev rpm cmake -DSET_TWEAK=Off -DBUILD_TESTS=On -DCMAKE_INSTALL_PREFIX=/usr . cmake --build . --target package --verbose -j4 ./fastfetch --list-features @@ -181,7 +181,7 @@ jobs: # CMake installed by apt has bug `list sub-command REMOVE_ITEM requires two or more arguments` wget --no-check-certificate https://apt.kitware.com/ubuntu/pool/main/c/cmake/{cmake_3.29.2-0kitware1ubuntu20.04.1_armhf.deb,cmake-data_3.29.2-0kitware1ubuntu20.04.1_all.deb} dpkg -i *.deb - apt-get install -y make g++ libvulkan-dev libwayland-dev libxrandr-dev libxcb-randr0-dev libdconf-dev libdbus-1-dev libmagickcore-dev libxfconf-0-dev libsqlite3-dev librpm-dev libegl-dev libglx-dev libosmesa6-dev ocl-icd-opencl-dev libnm-dev libpulse-dev libdrm-dev directx-headers-dev rpm + apt-get install -y make g++ libvulkan-dev libwayland-dev libxrandr-dev libxcb-randr0-dev libdconf-dev libdbus-1-dev libmagickcore-dev libxfconf-0-dev libsqlite3-dev librpm-dev libegl-dev libglx-dev libosmesa6-dev ocl-icd-opencl-dev libpulse-dev libdrm-dev directx-headers-dev rpm cmake -DSET_TWEAK=Off -DBUILD_TESTS=On -DCMAKE_INSTALL_PREFIX=/usr . cmake --build . --target package --verbose -j4 ./fastfetch --list-features @@ -216,7 +216,7 @@ jobs: githubToken: ${{ github.token }} run: | uname -a - apt-get update && apt-get install -y cmake make g++ libvulkan-dev libwayland-dev libxrandr-dev libxcb-randr0-dev libdconf-dev libdbus-1-dev libmagickcore-dev libxfconf-0-dev libsqlite3-dev librpm-dev libegl-dev libglx-dev libosmesa6-dev ocl-icd-opencl-dev libnm-dev libpulse-dev libdrm-dev libddcutil-dev libchafa-dev directx-headers-dev rpm + apt-get update && apt-get install -y cmake make g++ libvulkan-dev libwayland-dev libxrandr-dev libxcb-randr0-dev libdconf-dev libdbus-1-dev libmagickcore-dev libxfconf-0-dev libsqlite3-dev librpm-dev libegl-dev libglx-dev libosmesa6-dev ocl-icd-opencl-dev libpulse-dev libdrm-dev libddcutil-dev libchafa-dev directx-headers-dev rpm cmake -DSET_TWEAK=Off -DBUILD_TESTS=On -DCMAKE_INSTALL_PREFIX=/usr . cmake --build . --target package --verbose -j4 ./fastfetch --list-features diff --git a/CHANGELOG.md b/CHANGELOG.md index e38e1245d6..1a475abd0f 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,3 +1,40 @@ +# 2.21.0 + +Changes: +* We no longer use `libnm` for Wifi detection on Linux. Instead, we use `libdbus` to communicate with NetworkManager directly + * To package managers: libnm dependency should be removed + +Features: +* Add module `BluetoothRadio` that prints bluetooth radios installed on the system + * Don't confuse with module `Bluetooth` which lists connected bluetooth devices +* Detect more information when `--gpu-driver-specific` is used (GPU) +* Detect which type of nvidia driver (open source or proprietary) is used (GPU, Linux) +* `--gpu-driver-specific` adds supports for Moore Threads GPU (#1142, GPU, Linux / Windows) +* Use SetupAPI for detecting GPUs to support GPU detection when running fastfetch as a Windows Service (GPU, Windows) + * See https://github.com/gpustack/gpustack/pull/97#issuecomment-2264699787 for detail +* Detect playback status (Media, Linux) + +Bugfixes: +* Don't try to connect display server in tty mode (Linux, #1110) +* Improve ssh detection +* Fix max frequency printing in custom format (CPU) +* Fix displaying random characters when detecting kitty term font (#1136 / #1145, TerminalFont, Linux) +* Make sure to detect all physical memory devices (#1137) +* Don't detect `wl-restart` as WM (#1135, WM, Linux) +* Use PCI bus ID to match Nvidia cards; fix multi-GPU detection (GPU) +* Ignore invalid GPU (#1066, GPU, macOS) +* Print error when invalid color code is found (#1138) +* Fix invalid refresh rate detection on old macOS versions (Display, macOS) +* Fix disk size detection on 32-bit systems (Disk, BSD) +* Don't ignore disabled GPUs (#1140, GPU, Linux) +* Fix GPU type detection on FreeBSD (GPU, FreeBSD) +* Remove shell version detection for unknown shells (#1144, Shell) +* Don't detect hyfetch as shell on NixOS (Shell, NixOS) + +Logos: +* Update EndeavourOS_small +* Add QTS + # 2.20.0 This release fixes regression of `2.19.0` on M1 MacBook Air. It also introduces a new option `--key-type icon` to display predefined icons in keys (requires newest nerd font). See `fastfetch -h key-type` for detail. @@ -6,7 +43,7 @@ Changes: * JSON option `display.keyWidth` has been renamed to `display.key.width` * Previously: `{ "display": { "keyWidth": 3 } }` * Now: `{ "display": { "key": { "width": 3 } } }` -* Windows Terminal font detection **in WSL** has been removed due to [issue #1113](https://github.com/fastfetch/fastfetch/issues/1113) +* Windows Terminal font detection **in WSL** has been removed due to [issue #1113](https://github.com/fastfetch-cli/fastfetch/issues/1113) Features: * Add option `display.key.type: ` to print icons in keys diff --git a/CMakeLists.txt b/CMakeLists.txt index ebd32ab292..713ec814b8 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 2.20.0 + VERSION 2.21.0 LANGUAGES C DESCRIPTION "Fast neofetch-like system information tool" HOMEPAGE_URL "https://github.com/fastfetch-cli/fastfetch" @@ -17,7 +17,7 @@ if(ANDROID) elseif("${CMAKE_SYSTEM_NAME}" STREQUAL "Linux") set(LINUX TRUE CACHE BOOL "..." FORCE) # LINUX means GNU/Linux, not just the kernel elseif("${CMAKE_SYSTEM_NAME}" STREQUAL "FreeBSD") - set(BSD TRUE CACHE BOOL "..." FORCE) + set(FreeBSD TRUE CACHE BOOL "..." FORCE) elseif("${CMAKE_SYSTEM_NAME}" STREQUAL "SunOS") set(SunOS TRUE CACHE BOOL "..." FORCE) elseif(NOT APPLE AND NOT WIN32) @@ -44,28 +44,27 @@ include(CheckIncludeFile) include(CMakeDependentOption) -cmake_dependent_option(ENABLE_VULKAN "Enable vulkan" ON "LINUX OR APPLE OR BSD OR WIN32 OR ANDROID OR SunOS" OFF) -cmake_dependent_option(ENABLE_WAYLAND "Enable wayland-client" ON "LINUX OR BSD" OFF) -cmake_dependent_option(ENABLE_XCB_RANDR "Enable xcb-randr" ON "LINUX OR BSD OR SunOS" OFF) -cmake_dependent_option(ENABLE_XCB "Enable xcb" ON "LINUX OR BSD OR SunOS" OFF) -cmake_dependent_option(ENABLE_XRANDR "Enable xrandr" ON "LINUX OR BSD OR SunOS" OFF) -cmake_dependent_option(ENABLE_X11 "Enable x11" ON "LINUX OR BSD OR SunOS" OFF) -cmake_dependent_option(ENABLE_DRM "Enable libdrm" ON "LINUX OR BSD OR SunOS" OFF) -cmake_dependent_option(ENABLE_GIO "Enable gio-2.0" ON "LINUX OR BSD OR SunOS" OFF) -cmake_dependent_option(ENABLE_DCONF "Enable dconf" ON "LINUX OR BSD OR SunOS" OFF) -cmake_dependent_option(ENABLE_DBUS "Enable dbus-1" ON "LINUX OR BSD OR SunOS" OFF) -cmake_dependent_option(ENABLE_XFCONF "Enable libxfconf-0" ON "LINUX OR BSD OR SunOS" OFF) -cmake_dependent_option(ENABLE_SQLITE3 "Enable sqlite3" ON "LINUX OR BSD OR APPLE OR SunOS" OFF) +cmake_dependent_option(ENABLE_VULKAN "Enable vulkan" ON "LINUX OR APPLE OR FreeBSD OR WIN32 OR ANDROID OR SunOS" OFF) +cmake_dependent_option(ENABLE_WAYLAND "Enable wayland-client" ON "LINUX OR FreeBSD" OFF) +cmake_dependent_option(ENABLE_XCB_RANDR "Enable xcb-randr" ON "LINUX OR FreeBSD OR SunOS" OFF) +cmake_dependent_option(ENABLE_XCB "Enable xcb" ON "LINUX OR FreeBSD OR SunOS" OFF) +cmake_dependent_option(ENABLE_XRANDR "Enable xrandr" ON "LINUX OR FreeBSD OR SunOS" OFF) +cmake_dependent_option(ENABLE_X11 "Enable x11" ON "LINUX OR FreeBSD OR SunOS" OFF) +cmake_dependent_option(ENABLE_DRM "Enable libdrm" ON "LINUX OR FreeBSD OR SunOS" OFF) +cmake_dependent_option(ENABLE_GIO "Enable gio-2.0" ON "LINUX OR FreeBSD OR SunOS" OFF) +cmake_dependent_option(ENABLE_DCONF "Enable dconf" ON "LINUX OR FreeBSD OR SunOS" OFF) +cmake_dependent_option(ENABLE_DBUS "Enable dbus-1" ON "LINUX OR FreeBSD OR SunOS" OFF) +cmake_dependent_option(ENABLE_XFCONF "Enable libxfconf-0" ON "LINUX OR FreeBSD OR SunOS" OFF) +cmake_dependent_option(ENABLE_SQLITE3 "Enable sqlite3" ON "LINUX OR FreeBSD OR APPLE OR SunOS" OFF) cmake_dependent_option(ENABLE_RPM "Enable rpm" ON "LINUX" OFF) -cmake_dependent_option(ENABLE_IMAGEMAGICK7 "Enable imagemagick 7" ON "LINUX OR BSD OR APPLE OR WIN32 OR SunOS" OFF) -cmake_dependent_option(ENABLE_IMAGEMAGICK6 "Enable imagemagick 6" ON "LINUX OR BSD OR APPLE OR SunOS" OFF) +cmake_dependent_option(ENABLE_IMAGEMAGICK7 "Enable imagemagick 7" ON "LINUX OR FreeBSD OR APPLE OR WIN32 OR SunOS" OFF) +cmake_dependent_option(ENABLE_IMAGEMAGICK6 "Enable imagemagick 6" ON "LINUX OR FreeBSD OR APPLE OR SunOS" OFF) cmake_dependent_option(ENABLE_CHAFA "Enable chafa" ON "ENABLE_IMAGEMAGICK6 OR ENABLE_IMAGEMAGICK7" OFF) cmake_dependent_option(ENABLE_ZLIB "Enable zlib" ON "ENABLE_IMAGEMAGICK6 OR ENABLE_IMAGEMAGICK7" OFF) -cmake_dependent_option(ENABLE_EGL "Enable egl" ON "LINUX OR BSD OR WIN32 OR SunOS" OFF) -cmake_dependent_option(ENABLE_GLX "Enable glx" ON "LINUX OR BSD OR SunOS" OFF) -cmake_dependent_option(ENABLE_OSMESA "Enable osmesa" ON "LINUX OR BSD OR SunOS" OFF) -cmake_dependent_option(ENABLE_OPENCL "Enable opencl" ON "LINUX OR BSD OR WIN32 OR ANDROID OR SunOS" OFF) -cmake_dependent_option(ENABLE_LIBNM "Enable libnm" ON "LINUX" OFF) +cmake_dependent_option(ENABLE_EGL "Enable egl" ON "LINUX OR FreeBSD OR WIN32 OR SunOS" OFF) +cmake_dependent_option(ENABLE_GLX "Enable glx" ON "LINUX OR FreeBSD OR SunOS" OFF) +cmake_dependent_option(ENABLE_OSMESA "Enable osmesa" ON "LINUX OR FreeBSD OR SunOS" OFF) +cmake_dependent_option(ENABLE_OPENCL "Enable opencl" ON "LINUX OR FreeBSD OR WIN32 OR ANDROID OR SunOS" OFF) cmake_dependent_option(ENABLE_FREETYPE "Enable freetype" ON "ANDROID" OFF) cmake_dependent_option(ENABLE_PULSE "Enable pulse" ON "LINUX OR SunOS" OFF) cmake_dependent_option(ENABLE_DDCUTIL "Enable ddcutil" ON "LINUX" OFF) @@ -294,6 +293,7 @@ set(LIBFASTFETCH_SRC src/common/properties.c src/common/settings.c src/common/temps.c + src/detection/bluetoothradio/bluetoothradio.c src/detection/bootmgr/bootmgr.c src/detection/chassis/chassis.c src/detection/cpu/cpu.c @@ -326,6 +326,7 @@ set(LIBFASTFETCH_SRC src/modules/battery/battery.c src/modules/bios/bios.c src/modules/bluetooth/bluetooth.c + src/modules/bluetoothradio/bluetoothradio.c src/modules/board/board.c src/modules/bootmgr/bootmgr.c src/modules/brightness/brightness.c @@ -421,6 +422,7 @@ if(LINUX) src/detection/cpuusage/cpuusage_linux.c src/detection/cursor/cursor_linux.c src/detection/bluetooth/bluetooth_linux.c + src/detection/bluetoothradio/bluetoothradio_linux.c src/detection/disk/disk_linux.c src/detection/dns/dns_linux.c src/detection/physicaldisk/physicaldisk_linux.c @@ -488,6 +490,7 @@ elseif(ANDROID) src/detection/battery/battery_android.c src/detection/bios/bios_android.c src/detection/bluetooth/bluetooth_nosupport.c + src/detection/bluetoothradio/bluetoothradio_nosupport.c src/detection/board/board_android.c src/detection/bootmgr/bootmgr_nosupport.c src/detection/brightness/brightness_nosupport.c @@ -539,7 +542,7 @@ elseif(ANDROID) src/detection/camera/camera_android.c src/util/platform/FFPlatform_unix.c ) -elseif(BSD) +elseif(FreeBSD) list(APPEND LIBFASTFETCH_SRC src/common/dbus.c src/common/io/io_unix.c @@ -550,6 +553,7 @@ elseif(BSD) src/detection/battery/battery_bsd.c src/detection/bios/bios_bsd.c src/detection/bluetooth/bluetooth_linux.c + src/detection/bluetoothradio/bluetoothradio_linux.c src/detection/board/board_bsd.c src/detection/bootmgr/bootmgr_bsd.c src/detection/brightness/brightness_bsd.c @@ -626,6 +630,7 @@ elseif(APPLE) src/detection/battery/battery_apple.c src/detection/bios/bios_apple.c src/detection/bluetooth/bluetooth_apple.m + src/detection/bluetoothradio/bluetoothradio_apple.m src/detection/board/board_apple.c src/detection/bootmgr/bootmgr_apple.c src/detection/brightness/brightness_apple.c @@ -689,6 +694,7 @@ elseif(WIN32) src/detection/battery/battery_windows.c src/detection/bios/bios_windows.c src/detection/bluetooth/bluetooth_windows.c + src/detection/bluetoothradio/bluetoothradio_windows.c src/detection/board/board_windows.c src/detection/bootmgr/bootmgr_windows.c src/detection/brightness/brightness_windows.cpp @@ -763,6 +769,7 @@ elseif(SunOS) src/detection/cpuusage/cpuusage_sunos.c src/detection/cursor/cursor_linux.c src/detection/bluetooth/bluetooth_nosupport.c + src/detection/bluetoothradio/bluetoothradio_nosupport.c src/detection/disk/disk_sunos.c src/detection/dns/dns_linux.c src/detection/physicaldisk/physicaldisk_nosupport.c @@ -828,14 +835,14 @@ if(ENABLE_DIRECTX_HEADERS) endif() # Proprietary GPU driver APIs -if(LINUX OR BSD OR WIN32) +if(LINUX OR FreeBSD OR WIN32) list(APPEND LIBFASTFETCH_SRC src/detection/gpu/gpu_nvidia.c) + list(APPEND LIBFASTFETCH_SRC src/detection/gpu/gpu_mthreads.c) endif() if(WIN32) list(APPEND LIBFASTFETCH_SRC src/detection/gpu/gpu_intel.c) list(APPEND LIBFASTFETCH_SRC src/detection/gpu/gpu_amd.c) endif() - include(CheckFunctionExists) check_function_exists(wcwidth HAVE_WCWIDTH) if(NOT HAVE_WCWIDTH) @@ -888,7 +895,7 @@ if(APPLE AND EXISTS "/usr/bin/otool") target_compile_definitions(libfastfetch PUBLIC FF_LIBSYSTEM_VERSION="${CMAKE_MATCH_1}") endif() endif() -if(BSD AND EXISTS "/usr/local/bin/objdump") +if(FreeBSD AND EXISTS "/usr/local/bin/objdump") execute_process(COMMAND /bin/sh -c "/usr/local/bin/objdump -T /lib/libc.so.* | grep 'FBSD_[^ )]*' -o | sort -Vru | head -1" OUTPUT_VARIABLE OBJDUMP_T_RESULT) if("${OBJDUMP_T_RESULT}" MATCHES "FBSD_([0-9]+\\.[0-9]+)") @@ -1056,10 +1063,6 @@ ff_lib_enable(OPENCL "OpenCL" "OpenCL" ) -ff_lib_enable(LIBNM - "libnm" - "libnm" -) ff_lib_enable(FREETYPE "freetype2" "FreeType2" @@ -1125,7 +1128,7 @@ elseif(WIN32) PRIVATE "hid" PRIVATE "wtsapi32" ) -elseif(BSD) +elseif(FreeBSD) target_link_libraries(libfastfetch PRIVATE "m" PRIVATE "usbhid" @@ -1178,7 +1181,35 @@ if(WIN32) PRIVATE "-static" # stdc++, winpthread, gcc_s, etc. ) endif() - set(CMAKE_CXX_STANDARD 17) + set(CMAKE_CXX_STANDARD 17) +endif() +if(LINUX) + CHECK_INCLUDE_FILE("linux/videodev2.h" HAVE_LINUX_VIDEODEV2) + if(HAVE_LINUX_VIDEODEV2) + target_compile_definitions(libfastfetch PRIVATE FF_HAVE_LINUX_VIDEODEV2=1) + endif() + CHECK_INCLUDE_FILE("linux/wireless.h" HAVE_LINUX_WIRELESS) + if(HAVE_LINUX_WIRELESS) + target_compile_definitions(libfastfetch PRIVATE FF_HAVE_LINUX_WIRELESS=1) + endif() +endif() +if(NOT WIN32) + CHECK_INCLUDE_FILE("utmpx.h" HAVE_UTMPX) + if(HAVE_UTMPX) + target_compile_definitions(libfastfetch PRIVATE FF_HAVE_UTMPX=1) + endif() + CHECK_INCLUDE_FILE("wordexp.h" HAVE_WORDEXP) + if(HAVE_WORDEXP) + target_compile_definitions(libfastfetch PRIVATE FF_HAVE_WORDEXP=1) + endif() + CHECK_INCLUDE_FILE("pthread_np.h" HAVE_PTHREAD_NP) + if(HAVE_PTHREAD_NP) + target_compile_definitions(libfastfetch PRIVATE FF_HAVE_PTHREAD_NP=1) + endif() + check_function_exists("pthread_timedjoin_np" HAVE_TIMEDJOIN_NP) + if(HAVE_TIMEDJOIN_NP) + target_compile_definitions(libfastfetch PRIVATE FF_HAVE_TIMEDJOIN_NP=1) + endif() endif() ###################### diff --git a/debian/control b/debian/control index 262f6b9440..0f35707a52 100644 --- a/debian/control +++ b/debian/control @@ -2,7 +2,7 @@ Source: fastfetch Section: universe/utils Priority: optional Maintainer: Carter Li -Build-Depends: libvulkan-dev, libwayland-dev, libxrandr-dev, libxcb-randr0-dev, libdconf-dev, libdbus-1-dev, libmagickcore-dev, libxfconf-0-dev, libsqlite3-dev, librpm-dev, libegl-dev, libglx-dev, libosmesa6-dev, ocl-icd-opencl-dev, libnm-dev, libpulse-dev, libdrm-dev, libddcutil-dev, libchafa-dev, directx-headers-dev, pkgconf, cmake (>= 3.12), debhelper (>= 11.2), dh-cmake, dh-cmake-compat (= 1), dh-sequence-cmake, dh-sequence-ctest, ninja-build +Build-Depends: libvulkan-dev, libwayland-dev, libxrandr-dev, libxcb-randr0-dev, libdconf-dev, libdbus-1-dev, libmagickcore-dev, libxfconf-0-dev, libsqlite3-dev, librpm-dev, libegl-dev, libglx-dev, libosmesa6-dev, ocl-icd-opencl-dev, libpulse-dev, libdrm-dev, libddcutil-dev, libchafa-dev, directx-headers-dev, pkgconf, cmake (>= 3.12), debhelper (>= 11.2), dh-cmake, dh-cmake-compat (= 1), dh-sequence-cmake, dh-sequence-ctest, ninja-build Standards-Version: 4.0.0 Homepage: https://github.com/fastfetch-cli/fastfetch diff --git a/doc/json_schema.json b/doc/json_schema.json index 0d8773b93d..4af7a84df5 100644 --- a/doc/json_schema.json +++ b/doc/json_schema.json @@ -745,6 +745,7 @@ "battery", "bios", "bluetooth", + "bluetoothradio", "board", "bootmgr", "break", @@ -1049,7 +1050,7 @@ "additionalProperties": false, "properties": { "type": { - "description": "List bluetooth devices", + "description": "List (connected) bluetooth devices", "const": "bluetooth" }, "showDisconnected": { @@ -1080,6 +1081,35 @@ } } }, + { + "title": "Bluetooth Radio", + "type": "object", + "additionalProperties": false, + "properties": { + "type": { + "description": "List bluetooth radios width supported version and vendor", + "const": "bluetoothradio" + }, + "key": { + "$ref": "#/$defs/key" + }, + "keyColor": { + "$ref": "#/$defs/keyColor" + }, + "keyIcon": { + "$ref": "#/$defs/keyIcon" + }, + "keyWidth": { + "$ref": "#/$defs/keyWidth" + }, + "outputColor": { + "$ref": "#/$defs/outputColor" + }, + "format": { + "$ref": "#/$defs/format" + } + } + }, { "title": "Brightness", "type": "object", diff --git a/presets/all.jsonc b/presets/all.jsonc index 52d092d1c7..e19041c208 100644 --- a/presets/all.jsonc +++ b/presets/all.jsonc @@ -73,6 +73,7 @@ "opencl", "users", "bluetooth", + "bluetoothradio", "sound", "camera", "gamepad", diff --git a/presets/ci.jsonc b/presets/ci.jsonc index ac34edab2b..2f3512ce24 100644 --- a/presets/ci.jsonc +++ b/presets/ci.jsonc @@ -75,6 +75,7 @@ "opencl", "users", "bluetooth", + "bluetoothradio", "sound", "camera", "gamepad", diff --git a/presets/examples/20.jsonc b/presets/examples/20.jsonc index e64c0b0867..162e813f31 100644 --- a/presets/examples/20.jsonc +++ b/presets/examples/20.jsonc @@ -106,7 +106,7 @@ { "type": "cpu", "key": "│ CPU FREQ │{$1}", - "format": "{freq-max}{/freq-max}{freq-base}{/} GHz" + "format": "{freq-max}{/freq-max}{freq-base}{/}" }, { "type": "loadavg", diff --git a/src/common/dbus.c b/src/common/dbus.c index f5b40aee2a..553d7eff70 100644 --- a/src/common/dbus.c +++ b/src/common/dbus.c @@ -10,9 +10,8 @@ static bool loadLibSymbols(FFDBusLibrary* lib) FF_LIBRARY_LOAD(dbus, &instance.config.library.libDBus, false, "libdbus-1" FF_LIBRARY_EXTENSION, 4); FF_LIBRARY_LOAD_SYMBOL_PTR(dbus, lib, dbus_bus_get, false) FF_LIBRARY_LOAD_SYMBOL_PTR(dbus, lib, dbus_message_new_method_call, false) + FF_LIBRARY_LOAD_SYMBOL_PTR(dbus, lib, dbus_message_append_args, false) FF_LIBRARY_LOAD_SYMBOL_PTR(dbus, lib, dbus_message_iter_init, false) - FF_LIBRARY_LOAD_SYMBOL_PTR(dbus, lib, dbus_message_iter_init_append, false) - FF_LIBRARY_LOAD_SYMBOL_PTR(dbus, lib, dbus_message_iter_append_basic, false) FF_LIBRARY_LOAD_SYMBOL_PTR(dbus, lib, dbus_message_iter_get_arg_type, false) FF_LIBRARY_LOAD_SYMBOL_PTR(dbus, lib, dbus_message_iter_get_basic, false) FF_LIBRARY_LOAD_SYMBOL_PTR(dbus, lib, dbus_message_iter_recurse, false) @@ -20,10 +19,6 @@ static bool loadLibSymbols(FFDBusLibrary* lib) FF_LIBRARY_LOAD_SYMBOL_PTR(dbus, lib, dbus_message_iter_next, false) FF_LIBRARY_LOAD_SYMBOL_PTR(dbus, lib, dbus_message_unref, false) FF_LIBRARY_LOAD_SYMBOL_PTR(dbus, lib, dbus_connection_send_with_reply_and_block, false) - FF_LIBRARY_LOAD_SYMBOL_PTR(dbus, lib, dbus_connection_flush, false) - FF_LIBRARY_LOAD_SYMBOL_PTR(dbus, lib, dbus_pending_call_block, false) - FF_LIBRARY_LOAD_SYMBOL_PTR(dbus, lib, dbus_pending_call_steal_reply, false) - FF_LIBRARY_LOAD_SYMBOL_PTR(dbus, lib, dbus_pending_call_unref, false) dbus = NULL; // don't auto dlclose return true; } @@ -56,11 +51,11 @@ const char* ffDBusLoadData(DBusBusType busType, FFDBusData* data) return NULL; } -bool ffDBusGetValue(FFDBusData* dbus, DBusMessageIter* iter, FFstrbuf* result) +bool ffDBusGetString(FFDBusData* dbus, DBusMessageIter* iter, FFstrbuf* result) { int argType = dbus->lib->ffdbus_message_iter_get_arg_type(iter); - if(argType == DBUS_TYPE_STRING) + if(argType == DBUS_TYPE_STRING || argType == DBUS_TYPE_OBJECT_PATH) { const char* value = NULL; dbus->lib->ffdbus_message_iter_get_basic(iter, &value); @@ -72,6 +67,14 @@ bool ffDBusGetValue(FFDBusData* dbus, DBusMessageIter* iter, FFstrbuf* result) return true; } + if (argType == DBUS_TYPE_BYTE) + { + uint8_t value; + dbus->lib->ffdbus_message_iter_get_basic(iter, &value); + ffStrbufAppendC(result, (char) value); + return false; // Don't append a comma + } + if(argType != DBUS_TYPE_VARIANT && argType != DBUS_TYPE_ARRAY) return false; @@ -79,7 +82,7 @@ bool ffDBusGetValue(FFDBusData* dbus, DBusMessageIter* iter, FFstrbuf* result) dbus->lib->ffdbus_message_iter_recurse(iter, &subIter); if(argType == DBUS_TYPE_VARIANT) - return ffDBusGetValue(dbus, &subIter, result); + return ffDBusGetString(dbus, &subIter, result); //At this point we have an array @@ -87,13 +90,16 @@ bool ffDBusGetValue(FFDBusData* dbus, DBusMessageIter* iter, FFstrbuf* result) while(true) { - if(ffDBusGetValue(dbus, &subIter, result)) + if(ffDBusGetString(dbus, &subIter, result)) { foundAValue = true; ffStrbufAppendS(result, ", "); } - FF_DBUS_ITER_CONTINUE(dbus, &subIter); + if(!dbus->lib->ffdbus_message_iter_next(&subIter)) + break; + else + continue; } if(foundAValue) @@ -122,11 +128,27 @@ bool ffDBusGetBool(FFDBusData* dbus, DBusMessageIter* iter, bool* result) return ffDBusGetBool(dbus, &subIter, result); } -bool ffDBusGetByte(FFDBusData* dbus, DBusMessageIter* iter, uint8_t* result) +bool ffDBusGetUint(FFDBusData* dbus, DBusMessageIter* iter, uint32_t* result) { int argType = dbus->lib->ffdbus_message_iter_get_arg_type(iter); if(argType == DBUS_TYPE_BYTE) + { + uint8_t value = 0; + dbus->lib->ffdbus_message_iter_get_basic(iter, &value); + *result = value; + return true; + } + + if(argType == DBUS_TYPE_UINT16) + { + uint16_t value = 0; + dbus->lib->ffdbus_message_iter_get_basic(iter, &value); + *result = value; + return true; + } + + if(argType == DBUS_TYPE_UINT32) { dbus->lib->ffdbus_message_iter_get_basic(iter, result); return true; @@ -137,15 +159,18 @@ bool ffDBusGetByte(FFDBusData* dbus, DBusMessageIter* iter, uint8_t* result) DBusMessageIter subIter; dbus->lib->ffdbus_message_iter_recurse(iter, &subIter); - return ffDBusGetByte(dbus, &subIter, result); + return ffDBusGetUint(dbus, &subIter, result); } -DBusMessage* ffDBusGetMethodReply(FFDBusData* dbus, const char* busName, const char* objectPath, const char* interface, const char* method) +DBusMessage* ffDBusGetMethodReply(FFDBusData* dbus, const char* busName, const char* objectPath, const char* interface, const char* method, const char* arg) { DBusMessage* message = dbus->lib->ffdbus_message_new_method_call(busName, objectPath, interface, method); if(message == NULL) return NULL; + if (arg) + dbus->lib->ffdbus_message_append_args(message, DBUS_TYPE_STRING, &arg, DBUS_TYPE_INVALID); + DBusMessage* reply = dbus->lib->ffdbus_connection_send_with_reply_and_block(dbus->connection, message, FF_DBUS_TIMEOUT_MILLISECONDS, NULL); dbus->lib->ffdbus_message_unref(message); @@ -159,20 +184,10 @@ DBusMessage* ffDBusGetProperty(FFDBusData* dbus, const char* busName, const char if(message == NULL) return NULL; - DBusMessageIter requestIterator; - dbus->lib->ffdbus_message_iter_init_append(message, &requestIterator); - - if(!dbus->lib->ffdbus_message_iter_append_basic(&requestIterator, DBUS_TYPE_STRING, &interface)) - { - dbus->lib->ffdbus_message_unref(message); - return NULL; - } - - if(!dbus->lib->ffdbus_message_iter_append_basic(&requestIterator, DBUS_TYPE_STRING, &property)) - { - dbus->lib->ffdbus_message_unref(message); - return NULL; - } + dbus->lib->ffdbus_message_append_args(message, + DBUS_TYPE_STRING, &interface, + DBUS_TYPE_STRING, &property, + DBUS_TYPE_INVALID); DBusMessage* reply = dbus->lib->ffdbus_connection_send_with_reply_and_block(dbus->connection, message, FF_DBUS_TIMEOUT_MILLISECONDS, NULL); @@ -194,7 +209,27 @@ bool ffDBusGetPropertyString(FFDBusData* dbus, const char* busName, const char* return false; } - bool ret = ffDBusGetValue(dbus, &rootIterator, result); + bool ret = ffDBusGetString(dbus, &rootIterator, result); + + dbus->lib->ffdbus_message_unref(reply); + + return ret; +} + +bool ffDBusGetPropertyUint(FFDBusData* dbus, const char* busName, const char* objectPath, const char* interface, const char* property, uint32_t* result) +{ + DBusMessage* reply = ffDBusGetProperty(dbus, busName, objectPath, interface, property); + if(reply == NULL) + return false; + + DBusMessageIter rootIterator; + if(!dbus->lib->ffdbus_message_iter_init(reply, &rootIterator)) + { + dbus->lib->ffdbus_message_unref(reply); + return false; + } + + bool ret = ffDBusGetUint(dbus, &rootIterator, result); dbus->lib->ffdbus_message_unref(reply); diff --git a/src/common/dbus.h b/src/common/dbus.h index a327884d44..876ef355c6 100644 --- a/src/common/dbus.h +++ b/src/common/dbus.h @@ -7,22 +7,12 @@ #include "common/library.h" #define FF_DBUS_TIMEOUT_MILLISECONDS 35 - -#define FF_DBUS_ITER_CONTINUE(dbus, iterator) \ - { \ - if(!(dbus)->lib->ffdbus_message_iter_has_next(iterator)) \ - break; \ - (dbus)->lib->ffdbus_message_iter_next(iterator); \ - continue; \ - } - typedef struct FFDBusLibrary { FF_LIBRARY_SYMBOL(dbus_bus_get) FF_LIBRARY_SYMBOL(dbus_message_new_method_call) + FF_LIBRARY_SYMBOL(dbus_message_append_args) FF_LIBRARY_SYMBOL(dbus_message_iter_init) - FF_LIBRARY_SYMBOL(dbus_message_iter_init_append) - FF_LIBRARY_SYMBOL(dbus_message_iter_append_basic) FF_LIBRARY_SYMBOL(dbus_message_iter_get_arg_type) FF_LIBRARY_SYMBOL(dbus_message_iter_get_basic) FF_LIBRARY_SYMBOL(dbus_message_iter_recurse) @@ -30,10 +20,6 @@ typedef struct FFDBusLibrary FF_LIBRARY_SYMBOL(dbus_message_iter_next) FF_LIBRARY_SYMBOL(dbus_message_unref) FF_LIBRARY_SYMBOL(dbus_connection_send_with_reply_and_block) - FF_LIBRARY_SYMBOL(dbus_connection_flush) - FF_LIBRARY_SYMBOL(dbus_pending_call_block) - FF_LIBRARY_SYMBOL(dbus_pending_call_steal_reply) - FF_LIBRARY_SYMBOL(dbus_pending_call_unref) } FFDBusLibrary; typedef struct FFDBusData @@ -43,11 +29,12 @@ typedef struct FFDBusData } FFDBusData; const char* ffDBusLoadData(DBusBusType busType, FFDBusData* data); //Returns an error message or NULL on success -bool ffDBusGetValue(FFDBusData* dbus, DBusMessageIter* iter, FFstrbuf* result); +bool ffDBusGetString(FFDBusData* dbus, DBusMessageIter* iter, FFstrbuf* result); bool ffDBusGetBool(FFDBusData* dbus, DBusMessageIter* iter, bool* result); -bool ffDBusGetByte(FFDBusData* dbus, DBusMessageIter* iter, uint8_t* result); -DBusMessage* ffDBusGetMethodReply(FFDBusData* dbus, const char* busName, const char* objectPath, const char* interface, const char* method); +bool ffDBusGetUint(FFDBusData* dbus, DBusMessageIter* iter, uint32_t* result); +DBusMessage* ffDBusGetMethodReply(FFDBusData* dbus, const char* busName, const char* objectPath, const char* interface, const char* method, const char* arg); DBusMessage* ffDBusGetProperty(FFDBusData* dbus, const char* busName, const char* objectPath, const char* interface, const char* property); bool ffDBusGetPropertyString(FFDBusData* dbus, const char* busName, const char* objectPath, const char* interface, const char* property, FFstrbuf* result); +bool ffDBusGetPropertyUint(FFDBusData* dbus, const char* busName, const char* objectPath, const char* interface, const char* property, uint32_t* result); #endif // FF_HAVE_DBUS diff --git a/src/common/init.c b/src/common/init.c index 13d6f44682..96d1702918 100644 --- a/src/common/init.c +++ b/src/common/init.c @@ -173,94 +173,91 @@ void ffDestroyInstance(void) void ffListFeatures(void) { fputs( - #ifdef FF_HAVE_THREADS + #if FF_HAVE_THREADS "threads\n" #endif - #ifdef FF_HAVE_VULKAN + #if FF_HAVE_VULKAN "vulkan\n" #endif - #ifdef FF_HAVE_WAYLAND + #if FF_HAVE_WAYLAND "wayland\n" #endif - #ifdef FF_HAVE_XCB_RANDR + #if FF_HAVE_XCB_RANDR "xcb-randr\n" #endif - #ifdef FF_HAVE_XCB + #if FF_HAVE_XCB "xcb\n" #endif - #ifdef FF_HAVE_XRANDR + #if FF_HAVE_XRANDR "xrandr\n" #endif - #ifdef FF_HAVE_X11 + #if FF_HAVE_X11 "x11\n" #endif - #ifdef FF_HAVE_DRM + #if FF_HAVE_DRM "drm\n" #endif - #ifdef FF_HAVE_GIO + #if FF_HAVE_GIO "gio\n" #endif - #ifdef FF_HAVE_DCONF + #if FF_HAVE_DCONF "dconf\n" #endif - #ifdef FF_HAVE_DBUS + #if FF_HAVE_DBUS "dbus\n" #endif - #ifdef FF_HAVE_IMAGEMAGICK7 + #if FF_HAVE_IMAGEMAGICK7 "imagemagick7\n" #endif - #ifdef FF_HAVE_IMAGEMAGICK6 + #if FF_HAVE_IMAGEMAGICK6 "imagemagick6\n" #endif - #ifdef FF_HAVE_CHAFA + #if FF_HAVE_CHAFA "chafa\n" #endif - #ifdef FF_HAVE_ZLIB + #if FF_HAVE_ZLIB "zlib\n" #endif - #ifdef FF_HAVE_XFCONF + #if FF_HAVE_XFCONF "xfconf\n" #endif - #ifdef FF_HAVE_SQLITE3 + #if FF_HAVE_SQLITE3 "sqlite3\n" #endif - #ifdef FF_HAVE_RPM + #if FF_HAVE_RPM "rpm\n" #endif - #ifdef FF_HAVE_EGL + #if FF_HAVE_EGL "egl\n" #endif - #ifdef FF_HAVE_GLX + #if FF_HAVE_GLX "glx\n" #endif - #ifdef FF_HAVE_OSMESA + #if FF_HAVE_OSMESA "osmesa\n" #endif - #ifdef FF_HAVE_OPENCL + #if FF_HAVE_OPENCL "opencl\n" #endif - #ifdef FF_HAVE_FREETYPE + #if FF_HAVE_FREETYPE "freetype\n" #endif - #ifdef FF_HAVE_PULSE + #if FF_HAVE_PULSE "libpulse\n" #endif - #ifdef FF_HAVE_LIBNM - "libnm\n" - #endif - #ifdef FF_HAVE_DDCUTIL + #if FF_HAVE_DDCUTIL "libddcutil\n" #endif - #ifdef FF_HAVE_DIRECTX_HEADERS + #if FF_HAVE_DIRECTX_HEADERS "Directx Headers\n" #endif - #ifdef FF_USE_SYSTEM_YYJSON + #if FF_USE_SYSTEM_YYJSON "System yyjson\n" #endif - #if __has_include() + #if FF_HAVE_LINUX_VIDEODEV2 "linux/videodev2\n" #endif - #if __has_include() + #if FF_HAVE_LINUX_WIRELESS "linux/wireless\n" #endif "" diff --git a/src/common/io/io_unix.c b/src/common/io/io_unix.c index 788d47a3fc..d459c3d924 100644 --- a/src/common/io/io_unix.c +++ b/src/common/io/io_unix.c @@ -8,8 +8,10 @@ #include #include -#if __has_include() -#include +#if FF_HAVE_WORDEXP + #include +#else + #warning " not available" #endif static void createSubfolders(const char* fileName) @@ -112,7 +114,7 @@ bool ffPathExpandEnv(FF_MAYBE_UNUSED const char* in, FF_MAYBE_UNUSED FFstrbuf* o { bool result = false; - #if __has_include() // https://github.com/termux/termux-packages/pull/7056 + #if FF_HAVE_WORDEXP // https://github.com/termux/termux-packages/pull/7056 wordexp_t exp; if(wordexp(in, &exp, 0) != 0) @@ -131,46 +133,39 @@ bool ffPathExpandEnv(FF_MAYBE_UNUSED const char* in, FF_MAYBE_UNUSED FFstrbuf* o return result; } -const char* ffGetTerminalResponse(const char* request, const char* format, ...) +static int ftty = -1; +static struct termios oldTerm; +void restoreTerm(void) { - int fin = STDIN_FILENO, fout = STDOUT_FILENO; - FF_AUTO_CLOSE_FD int ftty = -1; + tcsetattr(ftty, TCSAFLUSH, &oldTerm); +} - if (!isatty(STDIN_FILENO)) - { - if (ftty < 0) - ftty = open("/dev/tty", O_RDWR | O_NOCTTY | O_CLOEXEC); - fin = ftty; - } - if (!isatty(STDOUT_FILENO)) +const char* ffGetTerminalResponse(const char* request, const char* format, ...) +{ + if (ftty < 0) { + ftty = open("/dev/tty", O_RDWR | O_NOCTTY | O_CLOEXEC); if (ftty < 0) - ftty = open("/dev/tty", O_RDWR | O_NOCTTY | O_CLOEXEC); - fout = ftty; - } + return "open(\"/dev/tty\", O_RDWR | O_NOCTTY | O_CLOEXEC) failed"; - struct termios oldTerm; - if(tcgetattr(fin, &oldTerm) == -1) - return "tcgetattr(STDIN_FILENO, &oldTerm) failed"; + if(tcgetattr(ftty, &oldTerm) == -1) + return "tcgetattr(STDIN_FILENO, &oldTerm) failed"; - struct termios newTerm = oldTerm; - newTerm.c_lflag &= (tcflag_t) ~(ICANON | ECHO); - if(tcsetattr(fin, TCSAFLUSH, &newTerm) == -1) - return "tcsetattr(STDIN_FILENO, TCSAFLUSH, &newTerm)"; + struct termios newTerm = oldTerm; + newTerm.c_lflag &= (tcflag_t) ~(ICANON | ECHO); + if(tcsetattr(ftty, TCSAFLUSH, &newTerm) == -1) + return "tcsetattr(STDIN_FILENO, TCSAFLUSH, &newTerm)"; + atexit(restoreTerm); + } - ffWriteFDData(fout, strlen(request), request); + ffWriteFDData(ftty, strlen(request), request); //Give the terminal some time to respond - if(poll(&(struct pollfd) { .fd = fin, .events = POLLIN }, 1, FF_IO_TERM_RESP_WAIT_MS) <= 0) - { - tcsetattr(fin, TCSANOW, &oldTerm); + if(poll(&(struct pollfd) { .fd = ftty, .events = POLLIN }, 1, FF_IO_TERM_RESP_WAIT_MS) <= 0) return "poll() timeout or failed"; - } char buffer[512]; - ssize_t bytesRead = read(fin, buffer, sizeof(buffer) - 1); - - tcsetattr(fin, TCSANOW, &oldTerm); + ssize_t bytesRead = read(ftty, buffer, sizeof(buffer) - 1); if(bytesRead <= 0) return "read(STDIN_FILENO, buffer, sizeof(buffer) - 1) failed"; diff --git a/src/common/modules.c b/src/common/modules.c index d7607aa3a1..3cd1589849 100644 --- a/src/common/modules.c +++ b/src/common/modules.c @@ -8,6 +8,7 @@ static FFModuleBaseInfo* B[] = { (void*) &instance.config.modules.battery, (void*) &instance.config.modules.bios, (void*) &instance.config.modules.bluetooth, + (void*) &instance.config.modules.bluetoothRadio, (void*) &instance.config.modules.board, (void*) &instance.config.modules.bootmgr, (void*) &instance.config.modules.break_, diff --git a/src/common/option.c b/src/common/option.c index a7453cd512..d2f2cd78d1 100644 --- a/src/common/option.c +++ b/src/common/option.c @@ -206,6 +206,11 @@ void ffOptionParseColorNoClear(const char* value, FFstrbuf* buffer) else FF_APPEND_COLOR_PROP_COND(title, colorTitle) else FF_APPEND_COLOR_PROP_COND(output, colorOutput) else FF_APPEND_COLOR_PROP_COND(separator, colorSeparator) + else + { + fprintf(stderr, "Error: invalid color code found: %s\n", value); + exit(479); + } } ffStrbufAppendC(buffer, *value); ++value; diff --git a/src/common/thread.h b/src/common/thread.h index 2fd328cf42..62c8cc0567 100644 --- a/src/common/thread.h +++ b/src/common/thread.h @@ -33,7 +33,7 @@ #else #include #include - #if __has_include() + #if FF_HAVE_PTHREAD_NP #include #endif #define FF_THREAD_MUTEX_INITIALIZER PTHREAD_MUTEX_INITIALIZER @@ -51,7 +51,7 @@ static inline void ffThreadDetach(FFThreadType thread) { pthread_detach(thread); } static inline bool ffThreadJoin(FFThreadType thread, FF_MAYBE_UNUSED uint32_t timeout) { - #if (defined(__linux__) && !defined(__ANDROID__)) || __has_include() + #if HAVE_TIMEDJOIN_NP if (timeout > 0) { struct timespec ts; diff --git a/src/detection/bluetooth/bluetooth_linux.c b/src/detection/bluetooth/bluetooth_linux.c index 1ed712c90a..b7ae5a8394 100644 --- a/src/detection/bluetooth/bluetooth_linux.c +++ b/src/detection/bluetooth/bluetooth_linux.c @@ -61,13 +61,17 @@ static void detectBluetoothValue(FFDBusData* dbus, DBusMessageIter* iter, FFBlue dbus->lib->ffdbus_message_iter_next(&dictIter); if(ffStrEquals(deviceProperty, "Address")) - ffDBusGetValue(dbus, &dictIter, &device->address); + ffDBusGetString(dbus, &dictIter, &device->address); else if(ffStrEquals(deviceProperty, "Name")) - ffDBusGetValue(dbus, &dictIter, &device->name); + ffDBusGetString(dbus, &dictIter, &device->name); else if(ffStrEquals(deviceProperty, "Icon")) - ffDBusGetValue(dbus, &dictIter, &device->type); + ffDBusGetString(dbus, &dictIter, &device->type); else if(ffStrEquals(deviceProperty, "Percentage")) - ffDBusGetByte(dbus, &dictIter, &device->battery); + { + uint32_t percentage; + if (ffDBusGetUint(dbus, &dictIter, &percentage)) + device->battery = (uint8_t) percentage; + } else if(ffStrEquals(deviceProperty, "Connected")) ffDBusGetBool(dbus, &dictIter, &device->connected); } @@ -86,8 +90,8 @@ static void detectBluetoothProperty(FFDBusData* dbus, DBusMessageIter* iter, FFB const char* propertyType; dbus->lib->ffdbus_message_iter_get_basic(&dictIter, &propertyType); - if(strstr(propertyType, "Device") == NULL && strstr(propertyType, "Battery") == NULL) - return; //We don't care about other properties + if(!ffStrContains(propertyType, ".Device") && !ffStrContains(propertyType, ".Battery")) + return; // We don't care about other properties dbus->lib->ffdbus_message_iter_next(&dictIter); @@ -97,11 +101,10 @@ static void detectBluetoothProperty(FFDBusData* dbus, DBusMessageIter* iter, FFB DBusMessageIter arrayIter; dbus->lib->ffdbus_message_iter_recurse(&dictIter, &arrayIter); - while(true) + do { detectBluetoothValue(dbus, &arrayIter, device); - FF_DBUS_ITER_CONTINUE(dbus, &arrayIter); - } + } while (dbus->lib->ffdbus_message_iter_next(&arrayIter)); } static void detectBluetoothObject(FFlist* devices, FFDBusData* dbus, DBusMessageIter* iter) @@ -118,8 +121,8 @@ static void detectBluetoothObject(FFlist* devices, FFDBusData* dbus, DBusMessage const char* objectPath; dbus->lib->ffdbus_message_iter_get_basic(&dictIter, &objectPath); - //We don't want adapter objects - if(strstr(objectPath, "dev_") == NULL) + // We don't want adapter objects + if(!ffStrContains(objectPath, "/dev_")) return; dbus->lib->ffdbus_message_iter_next(&dictIter); @@ -137,11 +140,10 @@ static void detectBluetoothObject(FFlist* devices, FFDBusData* dbus, DBusMessage device->battery = 0; device->connected = false; - while(true) + do { detectBluetoothProperty(dbus, &arrayIter, device); - FF_DBUS_ITER_CONTINUE(dbus, &arrayIter); - } + } while (dbus->lib->ffdbus_message_iter_next(&arrayIter)); if(device->name.length == 0) { @@ -160,11 +162,10 @@ static void detectBluetoothRoot(FFlist* devices, FFDBusData* dbus, DBusMessageIt DBusMessageIter arrayIter; dbus->lib->ffdbus_message_iter_recurse(iter, &arrayIter); - while(true) + do { detectBluetoothObject(devices, dbus, &arrayIter); - FF_DBUS_ITER_CONTINUE(dbus, &arrayIter); - } + } while (dbus->lib->ffdbus_message_iter_next(&arrayIter)); } static const char* detectBluetooth(FFlist* devices) @@ -174,7 +175,7 @@ static const char* detectBluetooth(FFlist* devices) if(error) return error; - DBusMessage* managedObjects = ffDBusGetMethodReply(&dbus, "org.bluez", "/", "org.freedesktop.DBus.ObjectManager", "GetManagedObjects"); + DBusMessage* managedObjects = ffDBusGetMethodReply(&dbus, "org.bluez", "/", "org.freedesktop.DBus.ObjectManager", "GetManagedObjects", NULL); if(!managedObjects) return "Failed to call GetManagedObjects"; diff --git a/src/detection/bluetooth/bluetooth_windows.c b/src/detection/bluetooth/bluetooth_windows.c index cf0c756b90..58322be190 100644 --- a/src/detection/bluetooth/bluetooth_windows.c +++ b/src/detection/bluetooth/bluetooth_windows.c @@ -15,43 +15,35 @@ const char* ffDetectBluetooth(FFlist* devices /* FFBluetoothResult */) FF_LIBRARY_LOAD_SYMBOL_MESSAGE(bluetoothapis, BluetoothFindNextDevice) FF_LIBRARY_LOAD_SYMBOL_MESSAGE(bluetoothapis, BluetoothFindDeviceClose) - BLUETOOTH_DEVICE_SEARCH_PARAMS btsp = { + BLUETOOTH_DEVICE_INFO btdi = { + .dwSize = sizeof(btdi) + }; + HBLUETOOTH_DEVICE_FIND hFind = ffBluetoothFindFirstDevice(&(BLUETOOTH_DEVICE_SEARCH_PARAMS) { .fReturnConnected = TRUE, .fReturnRemembered = TRUE, .fReturnAuthenticated = TRUE, .fReturnUnknown = TRUE, - .dwSize = sizeof(btsp) - }; - - BLUETOOTH_DEVICE_INFO btdi = { - .dwSize = sizeof(btdi) - }; - HBLUETOOTH_DEVICE_FIND hFind = ffBluetoothFindFirstDevice(&btsp, &btdi); + .dwSize = sizeof(BLUETOOTH_DEVICE_SEARCH_PARAMS) + }, &btdi); if(!hFind) { - if (GetLastError() == ERROR_NO_MORE_ITEMS) - return "No Bluetooth devices found"; - else - return "BluetoothFindFirstDevice() failed"; + return "BluetoothFindFirstDevice() failed"; } do { FFBluetoothResult* device = ffListAdd(devices); - ffStrbufInit(&device->name); - ffStrbufInit(&device->address); - ffStrbufInit(&device->type); - device->battery = 0; - device->connected = !!btdi.fConnected; - - ffStrbufSetWS(&device->name, btdi.szName); - - ffStrbufAppendF(&device->address, "%02x:%02x:%02x:%02x:%02x:%02x", + ffStrbufInitWS(&device->name, btdi.szName); + ffStrbufInitF(&device->address, "%02x:%02x:%02x:%02x:%02x:%02x", btdi.Address.rgBytes[0], btdi.Address.rgBytes[1], btdi.Address.rgBytes[2], btdi.Address.rgBytes[3], btdi.Address.rgBytes[4], btdi.Address.rgBytes[5]); + ffStrbufInit(&device->type); + device->battery = 0; + device->connected = !!btdi.fConnected; + //https://btprodspecificationrefs.blob.core.windows.net/assigned-numbers/Assigned%20Number%20Types/Assigned%20Numbers.pdf if(BitTest(&btdi.ulClassofDevice, 13)) diff --git a/src/detection/bluetoothradio/bluetoothradio.c b/src/detection/bluetoothradio/bluetoothradio.c new file mode 100644 index 0000000000..90a5b54539 --- /dev/null +++ b/src/detection/bluetoothradio/bluetoothradio.c @@ -0,0 +1,112 @@ +#include "bluetoothradio.h" + +// https://github.com/ziglang/zig/blob/a84951465b409495095a9598db0cae745f34fa7b/lib/libc/include/any-windows-any/bthdef.h#L187-L236 + +#define BTH_MFG_ERICSSON 0 +#define BTH_MFG_NOKIA 1 +#define BTH_MFG_INTEL 2 +#define BTH_MFG_IBM 3 +#define BTH_MFG_TOSHIBA 4 +#define BTH_MFG_3COM 5 +#define BTH_MFG_MICROSOFT 6 +#define BTH_MFG_LUCENT 7 +#define BTH_MFG_MOTOROLA 8 +#define BTH_MFG_INFINEON 9 +#define BTH_MFG_CSR 10 +#define BTH_MFG_SILICONWAVE 11 +#define BTH_MFG_DIGIANSWER 12 +#define BTH_MFG_TI 13 +#define BTH_MFG_PARTHUS 14 +#define BTH_MFG_BROADCOM 15 +#define BTH_MFG_MITEL 16 +#define BTH_MFG_WIDCOMM 17 +#define BTH_MFG_ZEEVO 18 +#define BTH_MFG_ATMEL 19 +#define BTH_MFG_MITSIBUSHI 20 +#define BTH_MFG_RTX_TELECOM 21 +#define BTH_MFG_KC_TECHNOLOGY 22 +#define BTH_MFG_NEWLOGIC 23 +#define BTH_MFG_TRANSILICA 24 +#define BTH_MFG_ROHDE_SCHWARZ 25 +#define BTH_MFG_TTPCOM 26 +#define BTH_MFG_SIGNIA 27 +#define BTH_MFG_CONEXANT 28 +#define BTH_MFG_QUALCOMM 29 +#define BTH_MFG_INVENTEL 30 +#define BTH_MFG_AVM_BERLIN 31 +#define BTH_MFG_BANDSPEED 32 +#define BTH_MFG_MANSELLA 33 +#define BTH_MFG_NEC 34 +#define BTH_MFG_WAVEPLUS_TECHNOLOGY_CO 35 +#define BTH_MFG_ALCATEL 36 +#define BTH_MFG_PHILIPS_SEMICONDUCTOR 37 +#define BTH_MFG_C_TECHNOLOGIES 38 +#define BTH_MFG_OPEN_INTERFACE 39 +#define BTH_MFG_RF_MICRO_DEVICES 40 +#define BTH_MFG_HITACHI 41 +#define BTH_MFG_SYMBOL_TECHNOLOGIES 42 +#define BTH_MFG_TENOVIS 43 +#define BTH_MFG_MACRONIX_INTERNATIONAL 44 +#define BTH_MFG_MARVELL 72 +#define BTH_MFG_APPLE 76 +#define BTH_MFG_NORDIC_SEMICONDUCTORS_ASA 89 +#define BTH_MFG_ARUBA_NETWORKS 283 +#define BTH_MFG_INTERNAL_USE 65535 + +const char* ffBluetoothRadioGetVendor(uint32_t manufacturerId) +{ + switch (manufacturerId) + { + case BTH_MFG_ERICSSON: return "Ericsson"; + case BTH_MFG_NOKIA: return "Nokia"; + case BTH_MFG_INTEL: return "Intel"; + case BTH_MFG_IBM: return "IBM"; + case BTH_MFG_TOSHIBA: return "Toshiba"; + case BTH_MFG_3COM: return "3Com"; + case BTH_MFG_MICROSOFT: return "Microsoft"; + case BTH_MFG_LUCENT: return "Lucent"; + case BTH_MFG_MOTOROLA: return "Motorola"; + case BTH_MFG_INFINEON: return "Infineon"; + case BTH_MFG_CSR: return "CSR"; + case BTH_MFG_SILICONWAVE: return "Silicon-Wave"; + case BTH_MFG_DIGIANSWER: return "Digi-Answer"; + case BTH_MFG_TI: return "Ti"; + case BTH_MFG_PARTHUS: return "Parthus"; + case BTH_MFG_BROADCOM: return "Broadcom"; + case BTH_MFG_MITEL: return "Mitel"; + case BTH_MFG_WIDCOMM: return "Widcomm"; + case BTH_MFG_ZEEVO: return "Zeevo"; + case BTH_MFG_ATMEL: return "Atmel"; + case BTH_MFG_MITSIBUSHI: return "Mitsubishi"; + case BTH_MFG_RTX_TELECOM: return "RTX Telecom"; + case BTH_MFG_KC_TECHNOLOGY: return "KC Technology"; + case BTH_MFG_NEWLOGIC: return "Newlogic"; + case BTH_MFG_TRANSILICA: return "Transilica"; + case BTH_MFG_ROHDE_SCHWARZ: return "Rohde-Schwarz"; + case BTH_MFG_TTPCOM: return "TTPCom"; + case BTH_MFG_SIGNIA: return "Signia"; + case BTH_MFG_CONEXANT: return "Conexant"; + case BTH_MFG_QUALCOMM: return "Qualcomm"; + case BTH_MFG_INVENTEL: return "Inventel"; + case BTH_MFG_AVM_BERLIN: return "AVM Berlin"; + case BTH_MFG_BANDSPEED: return "Bandspeed"; + case BTH_MFG_MANSELLA: return "Mansella"; + case BTH_MFG_NEC: return "NEC"; + case BTH_MFG_WAVEPLUS_TECHNOLOGY_CO: return "Waveplus"; + case BTH_MFG_ALCATEL: return "Alcatel"; + case BTH_MFG_PHILIPS_SEMICONDUCTOR: return "Philips Semiconductors"; + case BTH_MFG_C_TECHNOLOGIES: return "C Technologies"; + case BTH_MFG_OPEN_INTERFACE: return "Open Interface"; + case BTH_MFG_RF_MICRO_DEVICES: return "RF Micro Devices"; + case BTH_MFG_HITACHI: return "Hitachi"; + case BTH_MFG_SYMBOL_TECHNOLOGIES: return "Symbol Technologies"; + case BTH_MFG_TENOVIS: return "Tenovis"; + case BTH_MFG_MACRONIX_INTERNATIONAL: return "Macronix International"; + case BTH_MFG_MARVELL: return "Marvell"; + case BTH_MFG_APPLE: return "Apple"; + case BTH_MFG_NORDIC_SEMICONDUCTORS_ASA: return "Nordic Semiconductor ASA"; + case BTH_MFG_ARUBA_NETWORKS: return "Aruba Networks"; + case BTH_MFG_INTERNAL_USE: return "Internal Use"; + default: return "Unknown"; + } +} diff --git a/src/detection/bluetoothradio/bluetoothradio.h b/src/detection/bluetoothradio/bluetoothradio.h new file mode 100644 index 0000000000..46ea60f580 --- /dev/null +++ b/src/detection/bluetoothradio/bluetoothradio.h @@ -0,0 +1,18 @@ +#pragma once + +#include "fastfetch.h" + +typedef struct FFBluetoothRadioResult +{ + FFstrbuf name; + FFstrbuf address; + FFstrbuf vendor; + int32_t lmpVersion; + int32_t lmpSubversion; + bool enabled; + bool discoverable; + bool connectable; +} FFBluetoothRadioResult; + +const char* ffDetectBluetoothRadio(FFlist* devices /* FFBluetoothRadioResult */); +const char* ffBluetoothRadioGetVendor(uint32_t manufacturerId); diff --git a/src/detection/bluetoothradio/bluetoothradio_apple.m b/src/detection/bluetoothradio/bluetoothradio_apple.m new file mode 100644 index 0000000000..9c0426c71a --- /dev/null +++ b/src/detection/bluetoothradio/bluetoothradio_apple.m @@ -0,0 +1,69 @@ +#include "bluetoothradio.h" +#include "common/processing.h" + +#import + +// For some reason the official declaration of IOBluetoothHostController don't include property `controllers` +@interface IOBluetoothHostController() ++ (id)controllers; +@end + +const char* ffDetectBluetoothRadio(FFlist* devices /* FFBluetoothResult */) +{ + NSArray* ctrls = IOBluetoothHostController.controllers; + if(!ctrls) + return "IOBluetoothHostController.controllers returns nil"; + + FF_STRBUF_AUTO_DESTROY buffer = ffStrbufCreate(); + if (ffProcessAppendStdOut(&buffer, (char* const[]) { + "system_profiler", + "SPBluetoothDataType", + "-xml", + "-detailLevel", + "basic", + NULL + }) != NULL) + return "Starting `system_profiler SPBluetoothDataType -xml -detailLevel basic` failed"; + + NSArray* arr = [NSPropertyListSerialization propertyListWithData:[NSData dataWithBytes:buffer.chars length:buffer.length] + options:NSPropertyListImmutable + format:nil + error:nil]; + if (!arr || !arr.count) + return "system_profiler SPBluetoothDataType returned an empty array"; + + for (IOBluetoothHostController* ctrl in ctrls) + { + FFBluetoothRadioResult* device = ffListAdd(devices); + ffStrbufInitS(&device->name, ctrl.nameAsString.UTF8String); + ffStrbufInitS(&device->address, ctrl.addressAsString.UTF8String); + ffStrbufInitStatic(&device->vendor, "Apple"); + device->lmpVersion = INT_MIN; + device->lmpSubversion = INT_MIN; + device->enabled = ctrl.powerState == kBluetoothHCIPowerStateON; + device->discoverable = false; + device->connectable = true; + + for (NSDictionary* itemDict in arr[0][@"_items"]) + { + NSDictionary* props = itemDict[@"controller_properties"]; + if (!props) continue; + + if (![ctrl.addressAsString isEqualToString:props[@"controller_address"]]) continue; + + NSString* services = props[@"controller_supportedServices"]; + if ([services containsString:@" LEA "]) + device->lmpVersion = -11; + else if ([services containsString:@" GATT "]) + device->lmpVersion = -6; + + device->discoverable = ![props[@"controller_discoverable"] isEqualToString:@"attrib_off"]; + ffStrbufSetS(&device->vendor, ((NSString*) props[@"controller_vendorID"]).UTF8String); + ffStrbufSubstrAfterFirstC(&device->vendor, '('); + ffStrbufTrimRight(&device->vendor, ')'); + break; + } + } + + return NULL; +} diff --git a/src/detection/bluetoothradio/bluetoothradio_linux.c b/src/detection/bluetoothradio/bluetoothradio_linux.c new file mode 100644 index 0000000000..6157ee16bd --- /dev/null +++ b/src/detection/bluetoothradio/bluetoothradio_linux.c @@ -0,0 +1,207 @@ +#include "bluetoothradio.h" +#include "util/stringUtils.h" + +#ifdef FF_HAVE_DBUS +#include "common/dbus.h" + +/* Example dbus reply, striped to only the relevant parts: +array [ //root + dict entry( + object path "/org/bluez/hci0" + array [ + dict entry( + string "org.bluez.Adapter1" + array [ + dict entry( + string "Address" + variant string "XX:XX:XX:XX:XX:XX" + ) + dict entry( + string "Name" + variant string "xxxxxxxx" + ) + dict entry( + string "Powered" + variant boolean true + ) + dict entry( + string "PowerState" + variant string "on" + ) + dict entry( + string "Manufacturer" + variant uint16 2 + ) + dict entry( + string "Version" + variant byte 12 + ) + ] + ) + ] + ) +] +*/ + +static void detectBluetoothValue(FFDBusData* dbus, DBusMessageIter* iter, FFBluetoothRadioResult* device) +{ + if(dbus->lib->ffdbus_message_iter_get_arg_type(iter) != DBUS_TYPE_DICT_ENTRY) + return; + + DBusMessageIter dictIter; + dbus->lib->ffdbus_message_iter_recurse(iter, &dictIter); + + if(dbus->lib->ffdbus_message_iter_get_arg_type(&dictIter) != DBUS_TYPE_STRING) + return; + + const char* deviceProperty; + dbus->lib->ffdbus_message_iter_get_basic(&dictIter, &deviceProperty); + + dbus->lib->ffdbus_message_iter_next(&dictIter); + + if(ffStrEquals(deviceProperty, "Address")) + ffDBusGetString(dbus, &dictIter, &device->address); + else if(ffStrEquals(deviceProperty, "Alias")) + ffDBusGetString(dbus, &dictIter, &device->name); + else if(ffStrEquals(deviceProperty, "Manufacturer")) + { + uint32_t vendorId; + if (ffDBusGetUint(dbus, &dictIter, &vendorId)) + ffStrbufSetStatic(&device->vendor, ffBluetoothRadioGetVendor(vendorId)); + } + else if(ffStrEquals(deviceProperty, "Version")) + ffDBusGetUint(dbus, &dictIter, (uint32_t*) &device->lmpVersion); + else if(ffStrEquals(deviceProperty, "Powered")) + ffDBusGetBool(dbus, &dictIter, &device->enabled); + else if(ffStrEquals(deviceProperty, "Discoverable")) + ffDBusGetBool(dbus, &dictIter, &device->discoverable); + else if(ffStrEquals(deviceProperty, "Pairable")) + ffDBusGetBool(dbus, &dictIter, &device->connectable); +} + +static void detectBluetoothProperty(FFDBusData* dbus, DBusMessageIter* iter, FFBluetoothRadioResult* device) +{ + if(dbus->lib->ffdbus_message_iter_get_arg_type(iter) != DBUS_TYPE_DICT_ENTRY) + return; + + DBusMessageIter dictIter; + dbus->lib->ffdbus_message_iter_recurse(iter, &dictIter); + + if(dbus->lib->ffdbus_message_iter_get_arg_type(&dictIter) != DBUS_TYPE_STRING) + return; + + const char* propertyType; + dbus->lib->ffdbus_message_iter_get_basic(&dictIter, &propertyType); + + if(!ffStrContains(propertyType, ".Adapter")) + return; //We don't care about other properties + + dbus->lib->ffdbus_message_iter_next(&dictIter); + + if(dbus->lib->ffdbus_message_iter_get_arg_type(&dictIter) != DBUS_TYPE_ARRAY) + return; + + DBusMessageIter arrayIter; + dbus->lib->ffdbus_message_iter_recurse(&dictIter, &arrayIter); + + do + { + detectBluetoothValue(dbus, &arrayIter, device); + } while (dbus->lib->ffdbus_message_iter_next(&arrayIter)); +} + +static void detectBluetoothObject(FFlist* devices, FFDBusData* dbus, DBusMessageIter* iter) +{ + if(dbus->lib->ffdbus_message_iter_get_arg_type(iter) != DBUS_TYPE_DICT_ENTRY) + return; + + DBusMessageIter dictIter; + dbus->lib->ffdbus_message_iter_recurse(iter, &dictIter); + + if(dbus->lib->ffdbus_message_iter_get_arg_type(&dictIter) != DBUS_TYPE_OBJECT_PATH) + return; + + const char* objectPath; + dbus->lib->ffdbus_message_iter_get_basic(&dictIter, &objectPath); + + //We want adapter objects + if(!ffStrStartsWith(objectPath, "/org/bluez/hci") || ffStrContains(objectPath, "/dev_")) + return; + + dbus->lib->ffdbus_message_iter_next(&dictIter); + + if(dbus->lib->ffdbus_message_iter_get_arg_type(&dictIter) != DBUS_TYPE_ARRAY) + return; + + DBusMessageIter arrayIter; + dbus->lib->ffdbus_message_iter_recurse(&dictIter, &arrayIter); + + FFBluetoothRadioResult* device = ffListAdd(devices); + ffStrbufInit(&device->name); + ffStrbufInit(&device->address); + ffStrbufInitStatic(&device->vendor, "Unknown"); + device->lmpVersion = INT_MIN; + device->lmpSubversion = INT_MIN; + device->enabled = false; + + do + { + detectBluetoothProperty(dbus, &arrayIter, device); + } while (dbus->lib->ffdbus_message_iter_next(&arrayIter)); + + if(device->name.length == 0) + { + ffStrbufDestroy(&device->name); + ffStrbufDestroy(&device->address); + --devices->length; + } +} + +static void detectBluetoothRoot(FFlist* devices, FFDBusData* dbus, DBusMessageIter* iter) +{ + if(dbus->lib->ffdbus_message_iter_get_arg_type(iter) != DBUS_TYPE_ARRAY) + return; + + DBusMessageIter arrayIter; + dbus->lib->ffdbus_message_iter_recurse(iter, &arrayIter); + + do + { + detectBluetoothObject(devices, dbus, &arrayIter); + } while (dbus->lib->ffdbus_message_iter_next(&arrayIter)); +} + +static const char* detectBluetooth(FFlist* devices) +{ + FFDBusData dbus; + const char* error = ffDBusLoadData(DBUS_BUS_SYSTEM, &dbus); + if(error) + return error; + + DBusMessage* managedObjects = ffDBusGetMethodReply(&dbus, "org.bluez", "/", "org.freedesktop.DBus.ObjectManager", "GetManagedObjects", NULL); + if(!managedObjects) + return "Failed to call GetManagedObjects"; + + DBusMessageIter rootIter; + if(!dbus.lib->ffdbus_message_iter_init(managedObjects, &rootIter)) + { + dbus.lib->ffdbus_message_unref(managedObjects); + return "Failed to get root iterator of GetManagedObjects"; + } + + detectBluetoothRoot(devices, &dbus, &rootIter); + + dbus.lib->ffdbus_message_unref(managedObjects); + return NULL; +} + +#endif + +const char* ffDetectBluetoothRadio(FFlist* devices /* FFBluetoothRadioResult */) +{ + #ifdef FF_HAVE_DBUS + return detectBluetooth(devices); + #else + return "Fastfetch was compiled without DBus support"; + #endif +} diff --git a/src/detection/bluetoothradio/bluetoothradio_nosupport.c b/src/detection/bluetoothradio/bluetoothradio_nosupport.c new file mode 100644 index 0000000000..1df1ce0e30 --- /dev/null +++ b/src/detection/bluetoothradio/bluetoothradio_nosupport.c @@ -0,0 +1,6 @@ +#include "bluetoothradio.h" + +const char* ffDetectBluetoothRadio(FF_MAYBE_UNUSED FFlist* devices /* FFBluetoothRadioResult */) +{ + return "Not supported on this platform"; +} diff --git a/src/detection/bluetoothradio/bluetoothradio_windows.c b/src/detection/bluetoothradio/bluetoothradio_windows.c new file mode 100644 index 0000000000..6e6f9461b0 --- /dev/null +++ b/src/detection/bluetoothradio/bluetoothradio_windows.c @@ -0,0 +1,105 @@ +#include "bluetoothradio.h" +#include "common/library.h" +#include "common/io/io.h" +#include "util/windows/unicode.h" + +#include +#include +#include + +// #include + +#define BTH_IOCTL_BASE 0 +#define BTH_CTL(id) CTL_CODE(FILE_DEVICE_BLUETOOTH, (id), METHOD_BUFFERED, FILE_ANY_ACCESS) +#define IOCTL_BTH_GET_LOCAL_INFO BTH_CTL(BTH_IOCTL_BASE+0x00) +#define LMP_LE_SUPPORTED(x) ((x >> 38) & 1) + +typedef struct _BTH_RADIO_INFO +{ + // Supported LMP features of the radio. Use LMP_XXX() to extract + // the desired bits. + ULONGLONG lmpSupportedFeatures; + + // Manufacturer ID (possibly BTH_MFG_XXX) + USHORT mfg; + + // LMP subversion + USHORT lmpSubversion; + + // LMP version + UCHAR lmpVersion; +} __attribute__((__packed__)) BTH_RADIO_INFO; + +typedef struct _BTH_LOCAL_RADIO_INFO +{ + // Local BTH_ADDR, class of device, and radio name + BTH_DEVICE_INFO localInfo; + + // Combo of LOCAL_RADIO_XXX values + ULONG flags; + + // HCI revision, see core spec + USHORT hciRevision; + + // HCI version, see core spec + UCHAR hciVersion; + + // More information about the local radio (LMP, MFG) + BTH_RADIO_INFO radioInfo; +} __attribute__((__packed__)) BTH_LOCAL_RADIO_INFO; +static_assert(sizeof(BTH_LOCAL_RADIO_INFO) == 292, "BTH_LOCAL_RADIO_INFO should be 292 bytes"); + +#pragma GCC diagnostic ignored "-Wpointer-sign" + +const char* ffDetectBluetoothRadio(FFlist* devices /* FFBluetoothRadioResult */) +{ + // Actually bluetoothapis.dll, but it's missing on Windows 7 + FF_LIBRARY_LOAD(bluetoothapis, NULL, "dlopen bthprops.cpl failed", "bthprops.cpl", 1) + FF_LIBRARY_LOAD_SYMBOL_MESSAGE(bluetoothapis, BluetoothFindFirstRadio) + FF_LIBRARY_LOAD_SYMBOL_MESSAGE(bluetoothapis, BluetoothFindNextRadio) + FF_LIBRARY_LOAD_SYMBOL_MESSAGE(bluetoothapis, BluetoothFindRadioClose) + FF_LIBRARY_LOAD_SYMBOL_MESSAGE(bluetoothapis, BluetoothIsConnectable) + FF_LIBRARY_LOAD_SYMBOL_MESSAGE(bluetoothapis, BluetoothIsDiscoverable) + + HANDLE hRadio = NULL; + HBLUETOOTH_DEVICE_FIND hFind = ffBluetoothFindFirstRadio(&(BLUETOOTH_FIND_RADIO_PARAMS) { + .dwSize = sizeof(BLUETOOTH_FIND_RADIO_PARAMS) + }, &hRadio); + if(!hFind) + { + if (GetLastError() == ERROR_NO_MORE_ITEMS) + return "No Bluetooth radios found or service disabled"; + else + return "BluetoothFindFirstRadio() failed"; + } + + do { + BTH_LOCAL_RADIO_INFO blri; + DWORD returned; + if (!DeviceIoControl(hRadio, IOCTL_BTH_GET_LOCAL_INFO, NULL, 0, &blri, sizeof(blri), &returned, NULL)) + continue; + + FFBluetoothRadioResult* device = ffListAdd(devices); + ffStrbufInitS(&device->name, blri.localInfo.name); + + BLUETOOTH_ADDRESS_STRUCT addr = { .ullLong = blri.localInfo.address }; + ffStrbufInitF(&device->address, "%02x:%02x:%02x:%02x:%02x:%02x", + addr.rgBytes[0], + addr.rgBytes[1], + addr.rgBytes[2], + addr.rgBytes[3], + addr.rgBytes[4], + addr.rgBytes[5]); + + device->lmpVersion = blri.radioInfo.lmpVersion; + device->lmpSubversion = blri.radioInfo.lmpSubversion; + ffStrbufInitStatic(&device->vendor, ffBluetoothRadioGetVendor(blri.radioInfo.mfg)); + device->enabled = true; + device->connectable = ffBluetoothIsConnectable(hRadio); + device->discoverable = ffBluetoothIsDiscoverable(hRadio); + } while (ffBluetoothFindNextRadio(hFind, &hRadio)); + + ffBluetoothFindRadioClose(hFind); + + return NULL; +} diff --git a/src/detection/camera/camera_linux.c b/src/detection/camera/camera_linux.c index 080a322a99..d7e2988e43 100644 --- a/src/detection/camera/camera_linux.c +++ b/src/detection/camera/camera_linux.c @@ -5,13 +5,13 @@ #include #include -#if __has_include() +#if FF_HAVE_LINUX_VIDEODEV2 #include #endif const char* ffDetectCamera(FFlist* result) { -#if __has_include() +#if FF_HAVE_LINUX_VIDEODEV2 char path[] = "/dev/videoN"; for (uint32_t i = 0; i <= 9; ++i) diff --git a/src/detection/cpu/cpu_linux.c b/src/detection/cpu/cpu_linux.c index 851c881dbf..422f7f51d6 100644 --- a/src/detection/cpu/cpu_linux.c +++ b/src/detection/cpu/cpu_linux.c @@ -46,6 +46,8 @@ static void detectQualcomm(FFCPUResult* cpu) else if (ffStrbufEqualS(&cpu->name, "SM6450")) ffStrbufSetStatic(&cpu->name, "Qualcomm Snapdragon 6 Gen 1 [SM6450]"); + else if (ffStrbufEqualS(&cpu->name, "SM4635")) + ffStrbufSetStatic(&cpu->name, "Qualcomm Snapdragon 4s Gen 2 [SM4635]"); else if (ffStrbufEqualS(&cpu->name, "SM4450")) ffStrbufSetStatic(&cpu->name, "Qualcomm Snapdragon 4 Gen 2 [SM4450]"); else if (ffStrbufEqualS(&cpu->name, "SM4375")) diff --git a/src/detection/disk/disk_bsd.c b/src/detection/disk/disk_bsd.c index 3046a3577e..81be9c83fc 100644 --- a/src/detection/disk/disk_bsd.c +++ b/src/detection/disk/disk_bsd.c @@ -123,7 +123,7 @@ const char* ffDetectDisksImpl(FFDiskOptions* options, FFlist* disks) FFDisk* disk = ffListAdd(disks); - disk->bytesTotal = fs->f_blocks * fs->f_bsize; + disk->bytesTotal = (uint64_t)fs->f_blocks * fs->f_bsize; disk->bytesFree = (uint64_t)fs->f_bfree * fs->f_bsize; disk->bytesAvailable = (uint64_t)fs->f_bavail * fs->f_bsize; disk->bytesUsed = 0; // To be filled in ./disk.c diff --git a/src/detection/displayserver/displayserver_apple.c b/src/detection/displayserver/displayserver_apple.c index 15bba56b5d..d2e87ecf2a 100644 --- a/src/detection/displayserver/displayserver_apple.c +++ b/src/detection/displayserver/displayserver_apple.c @@ -37,7 +37,7 @@ static void detectDisplays(FFDisplayServerResult* ds) { const CVTime time = CVDisplayLinkGetNominalOutputVideoRefreshPeriod(link); if (!(time.flags & kCVTimeIsIndefinite)) - refreshRate = time.timeScale / (double) time.timeValue + 0.5; //59.97... + refreshRate = time.timeScale / (double) time.timeValue; //59.97... CVDisplayLinkRelease(link); } } @@ -86,21 +86,15 @@ static void detectDisplays(FFDisplayServerResult* ds) ); if (display) { - // Shitty code - uint8_t bitDepth = 0; - FF_CFTYPE_AUTO_RELEASE CFStringRef desc = CFCopyDescription(mode); - CFRange start = CFStringFind(desc, CFSTR("BitsPerSample = "), 0); - if (start.location != kCFNotFound) + // https://stackoverflow.com/a/33519316/9976392 + // Also shitty, but better than parsing `CFCopyDescription(mode)` + CFDictionaryRef dict = (CFDictionaryRef) *((int64_t *)mode + 2); + if (CFGetTypeID(dict) == CFDictionaryGetTypeID()) { - for (CFIndex idx = start.location + start.length; idx < CFStringGetLength(desc); ++idx) - { - UniChar ch = CFStringGetCharacterAtIndex(desc, idx); - if (!ffCharIsDigit((char) ch)) - break; - bitDepth = (uint8_t) (bitDepth * 10 + (ch - '0')); - } + int32_t bitDepth; + ffCfDictGetInt(dict, kCGDisplayBitsPerSample, &bitDepth); + display->bitDepth = (uint8_t) bitDepth; } - display->bitDepth = bitDepth; } CGDisplayModeRelease(mode); } diff --git a/src/detection/displayserver/linux/displayserver_linux.c b/src/detection/displayserver/linux/displayserver_linux.c index e864bdc07a..d8336f3a92 100644 --- a/src/detection/displayserver/linux/displayserver_linux.c +++ b/src/detection/displayserver/linux/displayserver_linux.c @@ -7,9 +7,48 @@ #include "common/settings.h" #endif +static void getWMProtocolNameFromEnv(FFDisplayServerResult* result) +{ + const char* env = getenv("XDG_SESSION_TYPE"); + if(env) + { + if(ffStrEqualsIgnCase(env, "wayland")) + ffStrbufSetS(&result->wmProtocolName, FF_WM_PROTOCOL_WAYLAND); + else if(ffStrEqualsIgnCase(env, "x11") || ffStrEqualsIgnCase(env, "xorg")) + ffStrbufSetS(&result->wmProtocolName, FF_WM_PROTOCOL_X11); + else if(ffStrEqualsIgnCase(env, "tty")) + ffStrbufSetS(&result->wmProtocolName, FF_WM_PROTOCOL_TTY); + else + ffStrbufSetS(&result->wmProtocolName, env); + + return; + } + + if(getenv("WAYLAND_DISPLAY") != NULL || getenv("WAYLAND_SOCKET") != NULL) + { + ffStrbufSetStatic(&result->wmProtocolName, FF_WM_PROTOCOL_WAYLAND); + return; + } + + if(getenv("DISPLAY") != NULL) // XWayland also set this + { + ffStrbufSetStatic(&result->wmProtocolName, FF_WM_PROTOCOL_X11); + return; + } + + env = getenv("TERM"); + if(ffStrSet(env) && ffStrEqualsIgnCase(env, "linux")) + { + ffStrbufSetStatic(&result->wmProtocolName, FF_WM_PROTOCOL_TTY); + return; + } +} + void ffConnectDisplayServerImpl(FFDisplayServerResult* ds) { - if (instance.config.general.dsForceDrm == FF_DS_FORCE_DRM_TYPE_FALSE) + getWMProtocolNameFromEnv(ds); + + if (!ffStrbufEqualS(&ds->wmProtocolName, FF_WM_PROTOCOL_TTY) &&instance.config.general.dsForceDrm == FF_DS_FORCE_DRM_TYPE_FALSE) { //We try wayland as our preferred display server, as it supports the most features. //This method can't detect the name of our WM / DE @@ -57,8 +96,11 @@ void ffConnectDisplayServerImpl(FFDisplayServerResult* ds) } #endif - //This fills in missing information about WM / DE by using env vars and iterating processes - ffdsDetectWMDE(ds); + if(!ffStrbufEqualS(&ds->wmProtocolName, FF_WM_PROTOCOL_TTY)) + { + //This fills in missing information about WM / DE by using env vars and iterating processes + ffdsDetectWMDE(ds); + } } bool ffdsMatchDrmConnector(const char* connName, FFstrbuf* edidName) diff --git a/src/detection/displayserver/linux/displayserver_linux.h b/src/detection/displayserver/linux/displayserver_linux.h index c4937ee227..2a97e70080 100644 --- a/src/detection/displayserver/linux/displayserver_linux.h +++ b/src/detection/displayserver/linux/displayserver_linux.h @@ -7,8 +7,7 @@ bool ffdsMatchDrmConnector(const char* connName, FFstrbuf* edidName); -const char* ffdsConnectWlroots(FFDisplayServerResult* result); -void ffdsConnectWayland(FFDisplayServerResult* result); +const char* ffdsConnectWayland(FFDisplayServerResult* result); void ffdsConnectXcbRandr(FFDisplayServerResult* result); void ffdsConnectXcb(FFDisplayServerResult* result); diff --git a/src/detection/displayserver/linux/wayland/global-output.c b/src/detection/displayserver/linux/wayland/global-output.c index 7f815f42f0..a8299d16d7 100644 --- a/src/detection/displayserver/linux/wayland/global-output.c +++ b/src/detection/displayserver/linux/wayland/global-output.c @@ -122,7 +122,7 @@ void ffWaylandHandleGlobalOutput(WaylandData* wldata, struct wl_registry* regist display.edidName.length ? &display.edidName // Try ignoring `eDP-1-unknown`, where `unknown` is localized - : display.description.length && !(ffStrbufStartsWithS(&display.description, display.name.chars) && display.description.chars[display.name.length] == '-') + : display.description.length && !ffStrbufContain(&display.description, &display.name) ? &display.description : &display.name, display.type, diff --git a/src/detection/displayserver/linux/wayland/wayland.c b/src/detection/displayserver/linux/wayland/wayland.c index 2bf03ebe7e..71671a74d5 100644 --- a/src/detection/displayserver/linux/wayland/wayland.c +++ b/src/detection/displayserver/linux/wayland/wayland.c @@ -22,14 +22,27 @@ static bool waylandDetectWM(int fd, FFDisplayServerResult* result) { struct ucred ucred; socklen_t len = sizeof(struct ucred); - if (getsockopt(fd, SOL_SOCKET, SO_PEERCRED, &ucred, &len) == -1) + if (getsockopt(fd, SOL_SOCKET, SO_PEERCRED, &ucred, &len) == -1 || ucred.pid <= 0) return false; FF_STRBUF_AUTO_DESTROY procPath = ffStrbufCreate(); ffStrbufAppendF(&procPath, "/proc/%d/cmdline", ucred.pid); //We check the cmdline for the process name, because it is not trimmed. - ffReadFileBuffer(procPath.chars, &result->wmProcessName); + if (!ffReadFileBuffer(procPath.chars, &result->wmProcessName)) + return false; + + // #1135: wl-restart is a special case + const char* filename = strrchr(result->wmProcessName.chars, '/'); + if (filename) + filename++; + else + filename = result->wmProcessName.chars; + + if (ffStrEquals(filename, "wl-restart")) + ffStrbufSubstrAfterFirstC(&result->wmProcessName, '\0'); + ffStrbufSubstrBeforeFirstC(&result->wmProcessName, '\0'); //Trim the arguments ffStrbufSubstrAfterLastC(&result->wmProcessName, '/'); //Trim the path + return true; } #else @@ -69,27 +82,93 @@ static void waylandGlobalAddListener(void* data, struct wl_registry* registry, u } } -bool detectWayland(FFDisplayServerResult* result) +void ffWaylandOutputNameListener(void* data, FF_MAYBE_UNUSED void* output, const char *name) +{ + WaylandDisplay* display = data; + if (display->id) return; + + display->type = ffdsGetDisplayType(name); + if (!display->edidName.length) + ffdsMatchDrmConnector(name, &display->edidName); + display->id = ffWaylandGenerateIdFromName(name); + ffStrbufAppendS(&display->name, name); +} + +void ffWaylandOutputDescriptionListener(void* data, FF_MAYBE_UNUSED void* output, const char* description) +{ + WaylandDisplay* display = data; + if (display->description.length) return; + + while (*description == ' ') ++description; + if (!ffStrEquals(description, "Unknown Display") && !ffStrContains(description, "(null)")) + ffStrbufAppendS(&display->description, description); +} + +uint32_t ffWaylandHandleRotation(WaylandDisplay* display) +{ + uint32_t rotation; + switch(display->transform) + { + case WL_OUTPUT_TRANSFORM_FLIPPED_90: + case WL_OUTPUT_TRANSFORM_90: + rotation = 90; + break; + case WL_OUTPUT_TRANSFORM_FLIPPED_180: + case WL_OUTPUT_TRANSFORM_180: + rotation = 180; + break; + case WL_OUTPUT_TRANSFORM_FLIPPED_270: + case WL_OUTPUT_TRANSFORM_270: + rotation = 270; + break; + default: + rotation = 0; + break; + } + + switch(rotation) + { + case 90: + case 270: { + int32_t temp = display->width; + display->width = display->height; + display->height = temp; + + temp = display->physicalWidth; + display->physicalWidth = display->physicalHeight; + display->physicalHeight = temp; + break; + } + default: + break; + } + return rotation; +} + +const char* ffdsConnectWayland(FFDisplayServerResult* result) { + if (getenv("XDG_RUNTIME_DIR") == NULL) + return "Wayland requires $XDG_RUNTIME_DIR being set"; + FF_LIBRARY_LOAD(wayland, &instance.config.library.libWayland, false, "libwayland-client" FF_LIBRARY_EXTENSION, 1) - FF_LIBRARY_LOAD_SYMBOL(wayland, wl_display_connect, false) - FF_LIBRARY_LOAD_SYMBOL(wayland, wl_display_get_fd, false) - FF_LIBRARY_LOAD_SYMBOL(wayland, wl_proxy_marshal_constructor, false) - FF_LIBRARY_LOAD_SYMBOL(wayland, wl_display_disconnect, false) - FF_LIBRARY_LOAD_SYMBOL(wayland, wl_registry_interface, false) + FF_LIBRARY_LOAD_SYMBOL_MESSAGE(wayland, wl_display_connect) + FF_LIBRARY_LOAD_SYMBOL_MESSAGE(wayland, wl_display_get_fd) + FF_LIBRARY_LOAD_SYMBOL_MESSAGE(wayland, wl_proxy_marshal_constructor) + FF_LIBRARY_LOAD_SYMBOL_MESSAGE(wayland, wl_display_disconnect) + FF_LIBRARY_LOAD_SYMBOL_MESSAGE(wayland, wl_registry_interface) WaylandData data = {}; - FF_LIBRARY_LOAD_SYMBOL_VAR(wayland, data, wl_proxy_marshal_constructor_versioned, false) - FF_LIBRARY_LOAD_SYMBOL_VAR(wayland, data, wl_proxy_add_listener, false) - FF_LIBRARY_LOAD_SYMBOL_VAR(wayland, data, wl_proxy_destroy, false) - FF_LIBRARY_LOAD_SYMBOL_VAR(wayland, data, wl_display_roundtrip, false) - FF_LIBRARY_LOAD_SYMBOL_VAR(wayland, data, wl_output_interface, false) + FF_LIBRARY_LOAD_SYMBOL_VAR_MESSAGE(wayland, data, wl_proxy_marshal_constructor_versioned) + FF_LIBRARY_LOAD_SYMBOL_VAR_MESSAGE(wayland, data, wl_proxy_add_listener) + FF_LIBRARY_LOAD_SYMBOL_VAR_MESSAGE(wayland, data, wl_proxy_destroy) + FF_LIBRARY_LOAD_SYMBOL_VAR_MESSAGE(wayland, data, wl_display_roundtrip) + FF_LIBRARY_LOAD_SYMBOL_VAR_MESSAGE(wayland, data, wl_output_interface) data.display = ffwl_display_connect(NULL); if(data.display == NULL) - return false; + return "wl_display_connect returned NULL"; waylandDetectWM(ffwl_display_get_fd(data.display), result); @@ -97,7 +176,7 @@ bool detectWayland(FFDisplayServerResult* result) if(registry == NULL) { ffwl_display_disconnect(data.display); - return false; + return "wl_display_get_registry returned NULL"; } data.result = result; @@ -192,97 +271,15 @@ bool detectWayland(FFDisplayServerResult* result) //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_WM_PROTOCOL_WAYLAND); - return true; + ffStrbufSetStatic(&result->wmProtocolName, FF_WM_PROTOCOL_WAYLAND); + return NULL; } -void ffWaylandOutputNameListener(void* data, FF_MAYBE_UNUSED void* output, const char *name) -{ - WaylandDisplay* display = data; - if (display->id) return; - - display->type = ffdsGetDisplayType(name); - if (!display->edidName.length) - ffdsMatchDrmConnector(name, &display->edidName); - display->id = ffWaylandGenerateIdFromName(name); - ffStrbufAppendS(&display->name, name); -} +#else -void ffWaylandOutputDescriptionListener(void* data, FF_MAYBE_UNUSED void* output, const char* description) +const char* ffdsConnectWayland(FF_MAYBE_UNUSED FFDisplayServerResult* result) { - WaylandDisplay* display = data; - if (display->description.length) return; - - while (*description == ' ') ++description; - if (!ffStrEquals(description, "Unknown Display") && !ffStrContains(description, "(null)")) - ffStrbufAppendS(&display->description, description); + return "Fastfetch was compiled without Wayland support"; } -uint32_t ffWaylandHandleRotation(WaylandDisplay* display) -{ - uint32_t rotation; - switch(display->transform) - { - case WL_OUTPUT_TRANSFORM_FLIPPED_90: - case WL_OUTPUT_TRANSFORM_90: - rotation = 90; - break; - case WL_OUTPUT_TRANSFORM_FLIPPED_180: - case WL_OUTPUT_TRANSFORM_180: - rotation = 180; - break; - case WL_OUTPUT_TRANSFORM_FLIPPED_270: - case WL_OUTPUT_TRANSFORM_270: - rotation = 270; - break; - default: - rotation = 0; - break; - } - - switch(rotation) - { - case 90: - case 270: { - int32_t temp = display->width; - display->width = display->height; - display->height = temp; - - temp = display->physicalWidth; - display->physicalWidth = display->physicalHeight; - display->physicalHeight = temp; - break; - } - default: - break; - } - return rotation; -} #endif - -void ffdsConnectWayland(FFDisplayServerResult* result) -{ - //Wayland requires this to be set - if(getenv("XDG_RUNTIME_DIR") == NULL) - return; - - #ifdef FF_HAVE_WAYLAND - if(detectWayland(result)) - return; - #endif - - const char* xdgSessionType = getenv("XDG_SESSION_TYPE"); - - //If XDG_SESSION_TYPE is set, and doesn't contain "wayland", we are probably not running in a wayland session. - if(xdgSessionType != NULL && !ffStrEqualsIgnCase(xdgSessionType, "wayland")) - return; - - //If XDG_SESSION_TYPE is not set, check if WAYLAND_DISPLAY or WAYLAND_SOCKET is set. - //If not, there is no indicator for a wayland session - if(xdgSessionType == NULL && getenv("WAYLAND_DISPLAY") == NULL && getenv("WAYLAND_SOCKET") == NULL) - return; - - //We are probably running a wayland compositor at this point, - //but fastfetch was compiled without the required library, or loading the library failed. - ffStrbufSetS(&result->wmProtocolName, FF_WM_PROTOCOL_WAYLAND); -} diff --git a/src/detection/displayserver/linux/wayland/zwlr-output.c b/src/detection/displayserver/linux/wayland/zwlr-output.c index 76aee392a7..dd31b91b0a 100644 --- a/src/detection/displayserver/linux/wayland/zwlr-output.c +++ b/src/detection/displayserver/linux/wayland/zwlr-output.c @@ -131,7 +131,7 @@ static void waylandHandleZwlrHead(void *data, FF_MAYBE_UNUSED struct zwlr_output rotation, display.edidName.length ? &display.edidName - : display.description.length + : display.description.length && !ffStrbufContain(&display.description, &display.name) ? &display.description : &display.name, display.type, diff --git a/src/detection/displayserver/linux/wmde.c b/src/detection/displayserver/linux/wmde.c index 0319cdd454..8f50b69ab1 100644 --- a/src/detection/displayserver/linux/wmde.c +++ b/src/detection/displayserver/linux/wmde.c @@ -240,38 +240,6 @@ static void applyPrettyNameIfDE(FFDisplayServerResult* result, const char* name) } } -static void getWMProtocolNameFromEnv(FFDisplayServerResult* result) -{ - //This is only called if all connection attempts to a display server failed - //We don't need to check for wayland here, as the wayland code will always set the protocol name to wayland - - const char* env = getenv("XDG_SESSION_TYPE"); - if(ffStrSet(env)) - { - if(ffStrEqualsIgnCase(env, "x11")) - ffStrbufSetS(&result->wmProtocolName, FF_WM_PROTOCOL_X11); - else if(ffStrEqualsIgnCase(env, "tty")) - ffStrbufSetS(&result->wmProtocolName, FF_WM_PROTOCOL_TTY); - else - ffStrbufSetS(&result->wmProtocolName, env); - - return; - } - - env = getenv("DISPLAY"); - if(ffStrSet(env)) - { - ffStrbufSetS(&result->wmProtocolName, FF_WM_PROTOCOL_X11); - return; - } - - env = getenv("TERM"); - if(ffStrSet(env) && ffStrEqualsIgnCase(env, "linux")) - { - ffStrbufSetS(&result->wmProtocolName, FF_WM_PROTOCOL_TTY); - return; - } -} static const char* getFromProcesses(FFDisplayServerResult* result) { @@ -396,15 +364,6 @@ static const char* getFromProcesses(FFDisplayServerResult* result) void ffdsDetectWMDE(FFDisplayServerResult* result) { - //If all connections failed, use the environment variables to detect protocol name - if(result->wmProtocolName.length == 0) - getWMProtocolNameFromEnv(result); - - //We don't want to detect anything in TTY - //This can't happen if a connection succeeded, so we don't need to clear wmProcessName - if(ffStrbufIgnCaseCompS(&result->wmProtocolName, FF_WM_PROTOCOL_TTY) == 0) - return; - const char* env = parseEnv(); if(result->wmProcessName.length > 0) diff --git a/src/detection/gpu/gpu.c b/src/detection/gpu/gpu.c index d9b30cd2e1..d1831f92a9 100644 --- a/src/detection/gpu/gpu.c +++ b/src/detection/gpu/gpu.c @@ -7,6 +7,7 @@ const char* FF_GPU_VENDOR_NAME_APPLE = "Apple"; const char* FF_GPU_VENDOR_NAME_AMD = "AMD"; const char* FF_GPU_VENDOR_NAME_INTEL = "Intel"; const char* FF_GPU_VENDOR_NAME_NVIDIA = "NVIDIA"; +const char* FF_GPU_VENDOR_NAME_MTHREADS = "Moore Threads"; const char* FF_GPU_VENDOR_NAME_QUALCOMM = "Qualcomm"; const char* FF_GPU_VENDOR_NAME_MTK = "MTK"; const char* FF_GPU_VENDOR_NAME_VMWARE = "VMware"; @@ -24,6 +25,7 @@ const char* ffGetGPUVendorString(unsigned vendorId) case 0x1002: case 0x1022: return FF_GPU_VENDOR_NAME_AMD; case 0x8086: case 0x8087: case 0x03e7: return FF_GPU_VENDOR_NAME_INTEL; case 0x0955: case 0x10de: case 0x12d2: return FF_GPU_VENDOR_NAME_NVIDIA; + case 0x1ed5: return FF_GPU_VENDOR_NAME_MTHREADS; case 0x5143: return FF_GPU_VENDOR_NAME_QUALCOMM; case 0x14c3: return FF_GPU_VENDOR_NAME_MTK; case 0x15ad: return FF_GPU_VENDOR_NAME_VMWARE; @@ -56,6 +58,7 @@ const char* detectByOpenGL(FFlist* gpus) gpu->temperature = FF_GPU_TEMP_UNSET; gpu->coreCount = FF_GPU_CORE_COUNT_UNSET; gpu->frequency = FF_GPU_FREQUENCY_UNSET; + gpu->coreUsage = FF_GPU_CORE_USAGE_UNSET; gpu->dedicated = gpu->shared = (FFGPUMemory){0, 0}; gpu->deviceId = 0; @@ -70,6 +73,8 @@ const char* detectByOpenGL(FFlist* gpus) ffStrbufSetStatic(&gpu->vendor, FF_GPU_VENDOR_NAME_AMD); else if (ffStrbufContainS(&gpu->name, "NVIDIA")) ffStrbufSetStatic(&gpu->vendor, FF_GPU_VENDOR_NAME_NVIDIA); + else if (ffStrbufContainS(&gpu->name, "MTT")) + ffStrbufSetStatic(&gpu->vendor, FF_GPU_VENDOR_NAME_MTHREADS); } diff --git a/src/detection/gpu/gpu.h b/src/detection/gpu/gpu.h index e3e50dacd5..6e999d6ee2 100644 --- a/src/detection/gpu/gpu.h +++ b/src/detection/gpu/gpu.h @@ -6,11 +6,13 @@ #define FF_GPU_CORE_COUNT_UNSET -1 #define FF_GPU_VMEM_SIZE_UNSET ((uint64_t)-1) #define FF_GPU_FREQUENCY_UNSET 0 +#define FF_GPU_CORE_USAGE_UNSET (0/0.0) extern const char* FF_GPU_VENDOR_NAME_APPLE; extern const char* FF_GPU_VENDOR_NAME_AMD; extern const char* FF_GPU_VENDOR_NAME_INTEL; extern const char* FF_GPU_VENDOR_NAME_NVIDIA; +extern const char* FF_GPU_VENDOR_NAME_MTHREADS; extern const char* FF_GPU_VENDOR_NAME_VMWARE; extern const char* FF_GPU_VENDOR_NAME_PARALLEL; extern const char* FF_GPU_VENDOR_NAME_MICROSOFT; @@ -35,6 +37,7 @@ typedef struct FFGPUResult uint32_t frequency; // Maximum time clock frequency in MHz FFGPUMemory dedicated; FFGPUMemory shared; + double coreUsage; uint64_t deviceId; // Used internally, may be uninitialized } FFGPUResult; diff --git a/src/detection/gpu/gpu_apple.c b/src/detection/gpu/gpu_apple.c index be74022c0e..1190a63676 100644 --- a/src/detection/gpu/gpu_apple.c +++ b/src/detection/gpu/gpu_apple.c @@ -76,8 +76,12 @@ static const char* detectFrequency(FFGPUResult* gpu) const char* ffDetectGPUImpl(const FFGPUOptions* options, FFlist* gpus) { FF_IOOBJECT_AUTO_RELEASE io_iterator_t iterator = IO_OBJECT_NULL; - if (IOServiceGetMatchingServices(MACH_PORT_NULL, IOServiceMatching(kIOAcceleratorClassName), &iterator) != kIOReturnSuccess) - return "IOServiceGetMatchingServices() failed"; + { + CFMutableDictionaryRef matches = IOServiceMatching(kIOAcceleratorClassName); + CFDictionaryAddValue(matches, CFSTR("IOMatchCategory"), CFSTR(kIOAcceleratorClassName)); + if (IOServiceGetMatchingServices(MACH_PORT_NULL, matches, &iterator) != kIOReturnSuccess) + return "IOServiceGetMatchingServices() failed"; + } io_registry_entry_t registryEntry; while ((registryEntry = IOIteratorNext(iterator)) != IO_OBJECT_NULL) @@ -103,6 +107,31 @@ const char* ffDetectGPUImpl(const FFGPUOptions* options, FFlist* gpus) if(ffCfDictGetInt(properties, CFSTR("gpu-core-count"), &gpu->coreCount)) // For Apple gpu->coreCount = FF_GPU_CORE_COUNT_UNSET; + gpu->coreUsage = 0.0/0.0; + CFDictionaryRef perfStatistics = NULL; + uint64_t vramUsed = 0, vramTotal = 0; + if (ffCfDictGetDict(properties, CFSTR("PerformanceStatistics"), &perfStatistics) == NULL) + { + int64_t utilization; + if (ffCfDictGetInt64(perfStatistics, CFSTR("Device Utilization %"), &utilization) == NULL) + gpu->coreUsage = (double) utilization; + else if (ffCfDictGetInt64(perfStatistics, CFSTR("GPU Core Utilization"), &utilization) == NULL) + gpu->coreUsage = (double) utilization / 10000000.; // Nvidia? + + if (ffCfDictGetInt64(perfStatistics, CFSTR("Alloc system memory"), (int64_t*) &vramTotal) == NULL) + { + if (ffCfDictGetInt64(perfStatistics, CFSTR("In use system memory"), (int64_t*) &vramUsed) != NULL) + vramTotal = 0; + } + else if (ffCfDictGetInt64(perfStatistics, CFSTR("vramUsedBytes"), (int64_t*) &vramTotal) == NULL) + { + if (ffCfDictGetInt64(perfStatistics, CFSTR("vramFreeBytes"), (int64_t*) &vramUsed) == NULL) + vramTotal += vramUsed; + else + vramTotal = 0; + } + } + ffStrbufInit(&gpu->name); //IOAccelerator returns model / vendor-id properties for Apple Silicon, but not for Intel Iris GPUs. //Still needs testing for AMD's @@ -123,7 +152,7 @@ const char* ffDetectGPUImpl(const FFGPUOptions* options, FFlist* gpus) ffStrbufInit(&gpu->vendor); int vendorId; - if(!ffCfDictGetInt(properties, CFSTR("vendor-id"), &vendorId)) + if(ffCfDictGetInt(properties, CFSTR("vendor-id"), &vendorId) == NULL) { const char* vendorStr = ffGetGPUVendorString((unsigned) vendorId); ffStrbufAppendS(&gpu->vendor, vendorStr); @@ -136,6 +165,17 @@ const char* ffDetectGPUImpl(const FFGPUOptions* options, FFlist* gpus) if (vendorStr == FF_GPU_VENDOR_NAME_APPLE) detectFrequency(gpu); #endif + + if (gpu->type == FF_GPU_TYPE_INTEGRATED) + { + gpu->shared.total = vramTotal; + gpu->shared.used = vramUsed; + } + else + { + gpu->dedicated.total = vramTotal; + gpu->dedicated.used = vramUsed; + } } gpu->temperature = options->temp ? detectGpuTemp(&gpu->name) : FF_GPU_TEMP_UNSET; diff --git a/src/detection/gpu/gpu_apple.m b/src/detection/gpu/gpu_apple.m index c63abaf5f5..e5b6bebf60 100644 --- a/src/detection/gpu/gpu_apple.m +++ b/src/detection/gpu/gpu_apple.m @@ -44,17 +44,6 @@ gpu->type = device.hasUnifiedMemory ? FF_GPU_TYPE_INTEGRATED : FF_GPU_TYPE_DISCRETE; #endif - - if (gpu->type == FF_GPU_TYPE_INTEGRATED) - { - gpu->shared.total = device.recommendedMaxWorkingSetSize; - gpu->shared.used = device.currentAllocatedSize; - } - else - { - gpu->dedicated.total = device.recommendedMaxWorkingSetSize; - gpu->dedicated.used = device.currentAllocatedSize; - } } return NULL; } diff --git a/src/detection/gpu/gpu_bsd.c b/src/detection/gpu/gpu_bsd.c index 7bc22b485c..519263556c 100644 --- a/src/detection/gpu/gpu_bsd.c +++ b/src/detection/gpu/gpu_bsd.c @@ -57,6 +57,7 @@ const char* ffDetectGPUImpl(const FFGPUOptions* options, FFlist* gpus) ffStrbufInit(&gpu->platformApi); gpu->temperature = FF_GPU_TEMP_UNSET; gpu->coreCount = FF_GPU_CORE_COUNT_UNSET; + gpu->coreUsage = FF_GPU_CORE_USAGE_UNSET; gpu->type = FF_GPU_TYPE_UNKNOWN; gpu->dedicated.total = gpu->dedicated.used = gpu->shared.total = gpu->shared.used = FF_GPU_VMEM_SIZE_UNSET; gpu->deviceId = ((uint64_t) pc->pc_sel.pc_domain << 6) | ((uint64_t) pc->pc_sel.pc_bus << 4) | ((uint64_t) pc->pc_sel.pc_dev << 2) | pc->pc_sel.pc_func; @@ -92,10 +93,29 @@ const char* ffDetectGPUImpl(const FFGPUOptions* options, FFlist* gpus) .coreCount = options->driverSpecific ? (uint32_t*) &gpu->coreCount : NULL, .type = &gpu->type, .frequency = &gpu->frequency, + .coreUsage = &gpu->coreUsage, + .name = options->driverSpecific ? &gpu->name : NULL, }, "libnvidia-ml.so"); + } - if (gpu->dedicated.total != FF_GPU_VMEM_SIZE_UNSET) - gpu->type = gpu->dedicated.total > (uint64_t)1024 * 1024 * 1024 ? FF_GPU_TYPE_DISCRETE : FF_GPU_TYPE_INTEGRATED; + if (gpu->type == FF_GPU_TYPE_UNKNOWN) + { + if (gpu->vendor.chars == FF_GPU_VENDOR_NAME_NVIDIA) + { + if (ffStrbufStartsWithIgnCaseS(&gpu->name, "GeForce") || + ffStrbufStartsWithIgnCaseS(&gpu->name, "Quadro") || + ffStrbufStartsWithIgnCaseS(&gpu->name, "Tesla")) + gpu->type = FF_GPU_TYPE_DISCRETE; + } + else if (gpu->vendor.chars == FF_GPU_VENDOR_NAME_MTHREADS) + { + if (ffStrbufStartsWithIgnCaseS(&gpu->name, "MTT ")) + gpu->type = FF_GPU_TYPE_DISCRETE; + } + else if (gpu->vendor.chars == FF_GPU_VENDOR_NAME_INTEL) + { + gpu->type = ffStrbufStartsWithIgnCaseS(&gpu->name, "Arc ") ? FF_GPU_TYPE_DISCRETE : FF_GPU_TYPE_INTEGRATED; + } } } diff --git a/src/detection/gpu/gpu_driver_specific.h b/src/detection/gpu/gpu_driver_specific.h index 8c935ff8c5..945b92cff6 100644 --- a/src/detection/gpu/gpu_driver_specific.h +++ b/src/detection/gpu/gpu_driver_specific.h @@ -40,10 +40,63 @@ typedef struct FFGpuDriverResult double* temp; FFGPUMemory* memory; uint32_t* coreCount; + double* coreUsage; FFGPUType* type; uint32_t* frequency; + FFstrbuf* name; } FFGpuDriverResult; const char* ffDetectNvidiaGpuInfo(const FFGpuDriverCondition* cond, FFGpuDriverResult result, const char* soName); const char* ffDetectIntelGpuInfo(const FFGpuDriverCondition* cond, FFGpuDriverResult result, const char* soName); const char* ffDetectAmdGpuInfo(const FFGpuDriverCondition* cond, FFGpuDriverResult result, const char* soName); +const char* ffDetectMthreadsGpuInfo(const FFGpuDriverCondition* cond, FFGpuDriverResult result, const char* soName); + +FF_MAYBE_UNUSED static inline bool getDriverSpecificDetectionFn(const char* vendor, __typeof__(&ffDetectNvidiaGpuInfo)* pDetectFn, const char** pDllName) +{ + if (vendor == FF_GPU_VENDOR_NAME_NVIDIA) + { + *pDetectFn = ffDetectNvidiaGpuInfo; + #ifdef _WIN32 + *pDllName = "nvml.dll"; + #else + *pDllName = "libnvidia-ml.so"; + #endif + } + else if (vendor == FF_GPU_VENDOR_NAME_MTHREADS) + { + *pDetectFn = ffDetectMthreadsGpuInfo; + #ifdef _WIN32 + *pDllName = "mtml.dll"; + #else + *pDllName = "libmtml.so"; + #endif + } + #ifdef _WIN32 + else if (vendor == FF_GPU_VENDOR_NAME_INTEL) + { + *pDetectFn = ffDetectIntelGpuInfo; + #ifdef _WIN64 + *pDllName = "ControlLib.dll"; + #else + *pDllName = "ControlLib32.dll"; + #endif + } + else if (vendor == FF_GPU_VENDOR_NAME_AMD) + { + *pDetectFn = ffDetectAmdGpuInfo; + #ifdef _WIN64 + *pDllName = "amd_ags_x64.dll"; + #else + *pDllName = "amd_ags_x86.dll"; + #endif + } + #endif + else + { + *pDetectFn = NULL; + *pDllName = NULL; + return false; + } + + return true; +} diff --git a/src/detection/gpu/gpu_linux.c b/src/detection/gpu/gpu_linux.c index 7b0fb54624..0dd04bc10c 100644 --- a/src/detection/gpu/gpu_linux.c +++ b/src/detection/gpu/gpu_linux.c @@ -7,29 +7,90 @@ #include "common/properties.h" #include "util/stringUtils.h" -#define FF_STR_INDIR(x) #x -#define FF_STR(x) FF_STR_INDIR(x) - #include -#if __has_include() - #include - #define FF_HAVE_DRM_H 1 -#elif __has_include() - #include - #define FF_HAVE_DRM_H 1 -#endif - -#if FF_HAVE_DRM_H +#if __aarch64__ && FF_HAVE_DRM + #include #include #include - #if __aarch64__ && __has_include() - #include - #define FF_HAVE_ASAHI_DRM_H 1 + #if __has_include() + #include + #else + /* SPDX-License-Identifier: MIT */ + /* Copyright (C) The Asahi Linux Contributors */ + #define DRM_ASAHI_GET_PARAMS 0x00 + #define DRM_ASAHI_MAX_CLUSTERS 32 + struct drm_asahi_params_global + { + __u32 unstable_uabi_version; + __u32 pad0; + + __u64 feat_compat; + __u64 feat_incompat; + + __u32 gpu_generation; + __u32 gpu_variant; + __u32 gpu_revision; + __u32 chip_id; + + __u32 num_dies; + __u32 num_clusters_total; + __u32 num_cores_per_cluster; + __u32 num_frags_per_cluster; + __u32 num_gps_per_cluster; + __u32 num_cores_total_active; + __u64 core_masks[DRM_ASAHI_MAX_CLUSTERS]; + + __u32 vm_page_size; + __u32 pad1; + __u64 vm_user_start; + __u64 vm_user_end; + __u64 vm_shader_start; + __u64 vm_shader_end; + + __u32 max_syncs_per_submission; + __u32 max_commands_per_submission; + __u32 max_commands_in_flight; + __u32 max_attachments; + + __u32 timer_frequency_hz; + __u32 min_frequency_khz; + __u32 max_frequency_khz; + __u32 max_power_mw; + + __u32 result_render_size; + __u32 result_compute_size; + }; + + struct drm_asahi_get_params + { + /** @extensions: Pointer to the first extension struct, if any */ + __u64 extensions; + + /** @param: Parameter group to fetch (MBZ) */ + __u32 param_group; + + /** @pad: MBZ */ + __u32 pad; + + /** @value: User pointer to write parameter struct */ + __u64 pointer; + + /** @value: Size of user buffer, max size supported on return */ + __u64 size; + }; + + enum + { + DRM_IOCTL_ASAHI_GET_PARAMS = DRM_IOWR(DRM_COMMAND_BASE + DRM_ASAHI_GET_PARAMS, struct drm_asahi_get_params), + }; #endif #endif +#define FF_STR_INDIR(x) #x +#define FF_STR(x) FF_STR_INDIR(x) + static bool pciDetectDriver(FFGPUResult* gpu, FFstrbuf* pciDir, FFstrbuf* buffer, FF_MAYBE_UNUSED const char* drmKey) { uint32_t pciDirLength = pciDir->length; @@ -45,6 +106,17 @@ static bool pciDetectDriver(FFGPUResult* gpu, FFstrbuf* pciDir, FFstrbuf* buffer ffStrbufSetNS(&gpu->driver, (uint32_t) (resultLength - (slash - pathBuf)), slash); } + if (ffStrbufEqualS(&gpu->driver, "nvidia")) + { + if (ffReadFileBuffer("/proc/driver/nvidia/version", buffer)) + { + if (ffStrbufContainS(buffer, " Open ")) + ffStrbufAppendS(&gpu->driver, " (open source)"); + else + ffStrbufAppendS(&gpu->driver, " (proprietary)"); + } + } + if (instance.config.general.detectVersion) { ffStrbufAppendS(pciDir, "/module/version"); @@ -212,14 +284,6 @@ static const char* detectPci(const FFGPUOptions* options, FFlist* gpus, FFstrbuf if (sscanf(pPciPath, "%" SCNx32 ":%" SCNx32 ":%" SCNx32 ".%" SCNx32, &pciDomain, &pciBus, &pciDevice, &pciFunc) != 4) return "Invalid PCI device path"; - ffStrbufAppendS(deviceDir, "/enable"); - if (ffReadFileBuffer(deviceDir->chars, buffer)) - { - if (!ffStrbufStartsWithC(buffer, '1')) - return "GPU disabled"; - } - ffStrbufSubstrBefore(deviceDir, drmDirPathLength); - FFGPUResult* gpu = (FFGPUResult*)ffListAdd(gpus); ffStrbufInitStatic(&gpu->vendor, ffGetGPUVendorString((uint16_t) vendorId)); ffStrbufInit(&gpu->name); @@ -279,11 +343,13 @@ static const char* detectPci(const FFGPUOptions* options, FFlist* gpus, FFstrbuf pciDetectIntelSpecific(gpu, deviceDir, buffer); ffStrbufSubstrBefore(deviceDir, drmDirPathLength); } - else if (gpu->vendor.chars == FF_GPU_VENDOR_NAME_NVIDIA) + else { - if (options->temp || options->driverSpecific) + __typeof__(&ffDetectNvidiaGpuInfo) detectFn; + const char* soName; + if (getDriverSpecificDetectionFn(gpu->vendor.chars, &detectFn, &soName) && (options->temp || options->driverSpecific)) { - ffDetectNvidiaGpuInfo(&(FFGpuDriverCondition) { + detectFn(&(FFGpuDriverCondition) { .type = FF_GPU_DRIVER_CONDITION_TYPE_BUS_ID, .pciBusId = { .domain = pciDomain, @@ -295,23 +361,35 @@ static const char* detectPci(const FFGPUOptions* options, FFlist* gpus, FFstrbuf .temp = options->temp ? &gpu->temperature : NULL, .memory = options->driverSpecific ? &gpu->dedicated : NULL, .coreCount = options->driverSpecific ? (uint32_t*) &gpu->coreCount : NULL, + .coreUsage = options->driverSpecific ? &gpu->coreUsage : NULL, .type = &gpu->type, .frequency = options->driverSpecific ? &gpu->frequency : NULL, - }, "libnvidia-ml.so"); + .name = options->driverSpecific ? &gpu->name : NULL, + }, soName); } if (gpu->type == FF_GPU_TYPE_UNKNOWN) { - if (ffStrbufStartsWithIgnCaseS(&gpu->name, "GeForce") || - ffStrbufStartsWithIgnCaseS(&gpu->name, "Quadro") || - ffStrbufStartsWithIgnCaseS(&gpu->name, "Tesla")) - gpu->type = FF_GPU_TYPE_DISCRETE; + if (gpu->vendor.chars == FF_GPU_VENDOR_NAME_NVIDIA) + { + if (ffStrbufStartsWithIgnCaseS(&gpu->name, "GeForce") || + ffStrbufStartsWithIgnCaseS(&gpu->name, "Quadro") || + ffStrbufStartsWithIgnCaseS(&gpu->name, "Tesla")) + gpu->type = FF_GPU_TYPE_DISCRETE; + } + else if (gpu->vendor.chars == FF_GPU_VENDOR_NAME_MTHREADS) + { + if (ffStrbufStartsWithIgnCaseS(&gpu->name, "MTT ")) + gpu->type = FF_GPU_TYPE_DISCRETE; + } } } return NULL; } +#if __aarch64__ + FF_MAYBE_UNUSED static const char* detectAsahi(FFlist* gpus, FFstrbuf* buffer, FFstrbuf* drmDir, const char* drmKey) { uint32_t index = ffStrbufFirstIndexS(buffer, "apple,agx-t"); @@ -326,13 +404,14 @@ FF_MAYBE_UNUSED static const char* detectAsahi(FFlist* gpus, FFstrbuf* buffer, F ffStrbufInitF(&gpu->platformApi, "DRM (%s)", drmKey); gpu->temperature = FF_GPU_TEMP_UNSET; gpu->coreCount = FF_GPU_CORE_COUNT_UNSET; + gpu->coreUsage = FF_GPU_CORE_USAGE_UNSET; gpu->type = FF_GPU_TYPE_INTEGRATED; gpu->dedicated.total = gpu->dedicated.used = gpu->shared.total = gpu->shared.used = FF_GPU_VMEM_SIZE_UNSET; gpu->frequency = FF_GPU_FREQUENCY_UNSET; pciDetectDriver(gpu, drmDir, buffer, drmKey); - #if FF_HAVE_ASAHI_DRM_H + #if FF_HAVE_DRM ffStrbufSetS(buffer, "/dev/dri/"); ffStrbufAppendS(buffer, drmKey); FF_AUTO_CLOSE_FD int fd = open(buffer->chars, O_RDONLY); @@ -354,6 +433,7 @@ FF_MAYBE_UNUSED static const char* detectAsahi(FFlist* gpus, FFstrbuf* buffer, F return NULL; } +#endif static const char* drmDetectGPUs(const FFGPUOptions* options, FFlist* gpus) { diff --git a/src/detection/gpu/gpu_mthreads.c b/src/detection/gpu/gpu_mthreads.c new file mode 100644 index 0000000000..aefc1d9da6 --- /dev/null +++ b/src/detection/gpu/gpu_mthreads.c @@ -0,0 +1,198 @@ +#include "gpu_driver_specific.h" + +#include "common/library.h" +#include "mtml.h" + +struct FFMtmlData +{ + FF_LIBRARY_SYMBOL(mtmlDeviceGetBrand) + FF_LIBRARY_SYMBOL(mtmlDeviceGetIndex) + FF_LIBRARY_SYMBOL(mtmlDeviceGetName) + FF_LIBRARY_SYMBOL(mtmlDeviceGetPciInfo) + FF_LIBRARY_SYMBOL(mtmlDeviceGetUUID) + FF_LIBRARY_SYMBOL(mtmlDeviceInitGpu) + FF_LIBRARY_SYMBOL(mtmlDeviceInitMemory) + FF_LIBRARY_SYMBOL(mtmlGpuGetMaxClock) + FF_LIBRARY_SYMBOL(mtmlGpuGetTemperature) + FF_LIBRARY_SYMBOL(mtmlGpuGetUtilization) + FF_LIBRARY_SYMBOL(mtmlLibraryCountDevice) + FF_LIBRARY_SYMBOL(mtmlLibraryInitDeviceByIndex) + FF_LIBRARY_SYMBOL(mtmlLibraryInitDeviceByPciSbdf) + FF_LIBRARY_SYMBOL(mtmlLibraryInitSystem) + FF_LIBRARY_SYMBOL(mtmlMemoryGetTotal) + FF_LIBRARY_SYMBOL(mtmlMemoryGetUsed) + FF_LIBRARY_SYMBOL(mtmlMemoryGetUtilization) + FF_LIBRARY_SYMBOL(mtmlLibraryShutDown) + + bool inited; + MtmlLibrary *lib; + MtmlSystem *sys; +} mtmlData; + +static void shutdownMtml() +{ + mtmlData.ffmtmlLibraryShutDown(mtmlData.lib); +} + +const char *ffDetectMthreadsGpuInfo(const FFGpuDriverCondition *cond, FFGpuDriverResult result, const char *soName) +{ +#ifndef FF_DISABLE_DLOPEN + + if (!mtmlData.inited) + { + mtmlData.inited = true; + FF_LIBRARY_LOAD(libmtml, NULL, "dlopen mtml failed", soName, 1); + FF_LIBRARY_LOAD_SYMBOL_MESSAGE(libmtml, mtmlLibraryInit) + FF_LIBRARY_LOAD_SYMBOL_VAR_MESSAGE(libmtml, mtmlData, mtmlDeviceGetBrand) + FF_LIBRARY_LOAD_SYMBOL_VAR_MESSAGE(libmtml, mtmlData, mtmlDeviceGetIndex) + FF_LIBRARY_LOAD_SYMBOL_VAR_MESSAGE(libmtml, mtmlData, mtmlDeviceGetName) + FF_LIBRARY_LOAD_SYMBOL_VAR_MESSAGE(libmtml, mtmlData, mtmlDeviceGetPciInfo) + FF_LIBRARY_LOAD_SYMBOL_VAR_MESSAGE(libmtml, mtmlData, mtmlDeviceGetUUID) + FF_LIBRARY_LOAD_SYMBOL_VAR_MESSAGE(libmtml, mtmlData, mtmlDeviceInitGpu) + FF_LIBRARY_LOAD_SYMBOL_VAR_MESSAGE(libmtml, mtmlData, mtmlDeviceInitMemory) + FF_LIBRARY_LOAD_SYMBOL_VAR_MESSAGE(libmtml, mtmlData, mtmlGpuGetMaxClock) + FF_LIBRARY_LOAD_SYMBOL_VAR_MESSAGE(libmtml, mtmlData, mtmlGpuGetTemperature) + FF_LIBRARY_LOAD_SYMBOL_VAR_MESSAGE(libmtml, mtmlData, mtmlGpuGetUtilization) + FF_LIBRARY_LOAD_SYMBOL_VAR_MESSAGE(libmtml, mtmlData, mtmlLibraryCountDevice) + FF_LIBRARY_LOAD_SYMBOL_VAR_MESSAGE(libmtml, mtmlData, mtmlLibraryInitDeviceByIndex) + FF_LIBRARY_LOAD_SYMBOL_VAR_MESSAGE(libmtml, mtmlData, mtmlLibraryInitDeviceByPciSbdf) + FF_LIBRARY_LOAD_SYMBOL_VAR_MESSAGE(libmtml, mtmlData, mtmlLibraryInitSystem) + FF_LIBRARY_LOAD_SYMBOL_VAR_MESSAGE(libmtml, mtmlData, mtmlMemoryGetTotal) + FF_LIBRARY_LOAD_SYMBOL_VAR_MESSAGE(libmtml, mtmlData, mtmlMemoryGetUsed) + FF_LIBRARY_LOAD_SYMBOL_VAR_MESSAGE(libmtml, mtmlData, mtmlMemoryGetUtilization) + FF_LIBRARY_LOAD_SYMBOL_VAR_MESSAGE(libmtml, mtmlData, mtmlLibraryShutDown) + + if (ffmtmlLibraryInit(&mtmlData.lib) != MTML_SUCCESS) + { + mtmlData.ffmtmlLibraryInitSystem = NULL; + return "mtmlLibraryInit failed"; + } + if (mtmlData.ffmtmlLibraryInitSystem(mtmlData.lib, &mtmlData.sys) != MTML_SUCCESS) + { + mtmlData.ffmtmlLibraryShutDown(mtmlData.lib); + mtmlData.ffmtmlLibraryInitSystem = NULL; + return "mtmlLibraryInitSystem failed"; + } + atexit(shutdownMtml); + libmtml = NULL; // don't close mtml + } + + if (mtmlData.ffmtmlLibraryInitSystem == NULL) + return "loading mtml library failed"; + + MtmlDevice *device = NULL; + if (cond->type & FF_GPU_DRIVER_CONDITION_TYPE_BUS_ID) + { + char pciBusIdStr[32]; + snprintf(pciBusIdStr, sizeof(pciBusIdStr) - 1, "%04x:%02x:%02x.%d", cond->pciBusId.domain, cond->pciBusId.bus, cond->pciBusId.device, cond->pciBusId.func); + + MtmlReturn ret = mtmlData.ffmtmlLibraryInitDeviceByPciSbdf(mtmlData.lib, pciBusIdStr, &device); + if (ret != MTML_SUCCESS) + return "mtmlLibraryInitDeviceByPciSbdf() failed"; + } + else if (cond->type & FF_GPU_DRIVER_CONDITION_TYPE_DEVICE_ID) + { + uint32_t count; + if (mtmlData.ffmtmlLibraryCountDevice(mtmlData.lib, &count) != MTML_SUCCESS) + return "mtmlLibraryCountDevice() failed"; + + for (uint32_t i = 0; i < count; i++, device = NULL) + { + if (mtmlData.ffmtmlLibraryInitDeviceByIndex(mtmlData.lib, i, &device) != MTML_SUCCESS) + continue; + + MtmlPciInfo pciInfo; + if (mtmlData.ffmtmlDeviceGetPciInfo(device, &pciInfo) != MTML_SUCCESS) + continue; + + if (pciInfo.pciDeviceId != ((cond->pciDeviceId.deviceId << 16u) | cond->pciDeviceId.vendorId) || + pciInfo.pciSubsystemId != cond->pciDeviceId.subSystemId) + continue; + + break; + } + if (!device) + return "Device not found"; + } + else + { + return "Unknown condition type"; + } + + MtmlBrandType brand; + if (mtmlData.ffmtmlDeviceGetBrand(device, &brand) == MTML_SUCCESS) + { + switch (brand) + { + case MTML_BRAND_MTT: + *result.type = FF_GPU_TYPE_DISCRETE; + break; + default: + break; + } + } + + if (result.temp) + { + MtmlGpu *gpu = NULL; + if (mtmlData.ffmtmlDeviceInitGpu(device, &gpu) == MTML_SUCCESS) + { + uint32_t value; + if (mtmlData.ffmtmlGpuGetTemperature(gpu, &value) == MTML_SUCCESS) + *result.temp = value; + } + } + + if (result.memory) + { + MtmlMemory *mem = NULL; + if (mtmlData.ffmtmlDeviceInitMemory(device, &mem) == MTML_SUCCESS) + { + unsigned long long total; + if (mtmlData.ffmtmlMemoryGetTotal(mem, &total) == MTML_SUCCESS) + result.memory->total = total; + + unsigned long long used; + if (mtmlData.ffmtmlMemoryGetUsed(mem, &used) == MTML_SUCCESS) + result.memory->used = used; + } + } + + if (result.frequency) + { + MtmlGpu *gpu = NULL; + if (mtmlData.ffmtmlDeviceInitGpu(device, &gpu) == MTML_SUCCESS) + { + uint32_t clockMHz; + if (mtmlData.ffmtmlGpuGetMaxClock(gpu, &clockMHz) == MTML_SUCCESS) + *result.frequency = clockMHz; + } + } + + if (result.coreUsage) + { + MtmlGpu *gpu = NULL; + if (mtmlData.ffmtmlDeviceInitGpu(device, &gpu) == MTML_SUCCESS) + { + unsigned int utilization; + if (mtmlData.ffmtmlGpuGetUtilization(gpu, &utilization) == MTML_SUCCESS) + *result.coreUsage = utilization; + } + } + + if (result.name) + { + char name[MTML_DEVICE_NAME_BUFFER_SIZE]; + if (mtmlData.ffmtmlDeviceGetName(device, name, sizeof(name)) == MTML_SUCCESS) + ffStrbufSetS(result.name, name); + } + + return NULL; + +#else + + FF_UNUSED(cond, result, soName); + return "dlopen is disabled"; + +#endif +} diff --git a/src/detection/gpu/gpu_nvidia.c b/src/detection/gpu/gpu_nvidia.c index 683e681ef2..42ed09d91a 100644 --- a/src/detection/gpu/gpu_nvidia.c +++ b/src/detection/gpu/gpu_nvidia.c @@ -12,7 +12,9 @@ struct FFNvmlData { FF_LIBRARY_SYMBOL(nvmlDeviceGetMemoryInfo_v2) FF_LIBRARY_SYMBOL(nvmlDeviceGetNumGpuCores) FF_LIBRARY_SYMBOL(nvmlDeviceGetMaxClockInfo) + FF_LIBRARY_SYMBOL(nvmlDeviceGetUtilizationRates) FF_LIBRARY_SYMBOL(nvmlDeviceGetBrand) + FF_LIBRARY_SYMBOL(nvmlDeviceGetName) bool inited; } nvmlData; @@ -35,7 +37,9 @@ const char* ffDetectNvidiaGpuInfo(const FFGpuDriverCondition* cond, FFGpuDriverR FF_LIBRARY_LOAD_SYMBOL_VAR_MESSAGE(libnvml, nvmlData, nvmlDeviceGetMemoryInfo_v2) FF_LIBRARY_LOAD_SYMBOL_VAR_MESSAGE(libnvml, nvmlData, nvmlDeviceGetNumGpuCores) FF_LIBRARY_LOAD_SYMBOL_VAR_MESSAGE(libnvml, nvmlData, nvmlDeviceGetMaxClockInfo) + FF_LIBRARY_LOAD_SYMBOL_VAR_MESSAGE(libnvml, nvmlData, nvmlDeviceGetUtilizationRates) FF_LIBRARY_LOAD_SYMBOL_VAR_MESSAGE(libnvml, nvmlData, nvmlDeviceGetBrand) + FF_LIBRARY_LOAD_SYMBOL_VAR_MESSAGE(libnvml, nvmlData, nvmlDeviceGetName) if (ffnvmlInit_v2() != NVML_SUCCESS) { @@ -124,6 +128,20 @@ const char* ffDetectNvidiaGpuInfo(const FFGpuDriverCondition* cond, FFGpuDriverR if (result.frequency) nvmlData.ffnvmlDeviceGetMaxClockInfo(device, NVML_CLOCK_GRAPHICS, result.frequency); + if (result.coreUsage) + { + nvmlUtilization_t utilization; + if (nvmlData.ffnvmlDeviceGetUtilizationRates(device, &utilization) == NVML_SUCCESS) + *result.coreUsage = utilization.gpu; + } + + if (result.name) + { + char name[NVML_DEVICE_NAME_V2_BUFFER_SIZE]; + if (nvmlData.ffnvmlDeviceGetName(device, name, sizeof(name)) == NVML_SUCCESS) + ffStrbufSetS(result.name, name); + } + return NULL; #else diff --git a/src/detection/gpu/gpu_sunos.c b/src/detection/gpu/gpu_sunos.c index a040adf1f2..168cfaac15 100644 --- a/src/detection/gpu/gpu_sunos.c +++ b/src/detection/gpu/gpu_sunos.c @@ -59,6 +59,7 @@ const char* ffDetectGPUImpl(FF_MAYBE_UNUSED const FFGPUOptions* options, FFlist* ffStrbufInit(&gpu->platformApi); gpu->temperature = FF_GPU_TEMP_UNSET; gpu->coreCount = FF_GPU_CORE_COUNT_UNSET; + gpu->coreUsage = FF_GPU_CORE_USAGE_UNSET; gpu->type = FF_GPU_TYPE_UNKNOWN; gpu->dedicated.total = gpu->dedicated.used = gpu->shared.total = gpu->shared.used = FF_GPU_VMEM_SIZE_UNSET; gpu->deviceId = 0; diff --git a/src/detection/gpu/gpu_windows.c b/src/detection/gpu/gpu_windows.c index 34ecaa5379..e9d4ffbb1c 100644 --- a/src/detection/gpu/gpu_windows.c +++ b/src/detection/gpu/gpu_windows.c @@ -3,140 +3,118 @@ #include "util/windows/unicode.h" #include "util/windows/registry.h" -#include +#define INITGUID +#include +#include +#include -static int isGpuNameEqual(const FFGPUResult* gpu, const FFstrbuf* name) +static inline void wrapSetupDiDestroyDeviceInfoList(HDEVINFO* hdev) { - return ffStrbufEqual(&gpu->name, name); + if(*hdev) + SetupDiDestroyDeviceInfoList(*hdev); } -static inline bool getDriverSpecificDetectionFn(const char* vendor, __typeof__(&ffDetectNvidiaGpuInfo)* pDetectFn, const char** pDllName) -{ - if (vendor == FF_GPU_VENDOR_NAME_NVIDIA) - { - *pDetectFn = ffDetectNvidiaGpuInfo; - *pDllName = "nvml.dll"; - } - else if (vendor == FF_GPU_VENDOR_NAME_INTEL) - { - *pDetectFn = ffDetectIntelGpuInfo; - #ifdef _WIN64 - *pDllName = "ControlLib.dll"; - #else - *pDllName = "ControlLib32.dll"; - #endif - } - else if (vendor == FF_GPU_VENDOR_NAME_AMD) - { - *pDetectFn = ffDetectAmdGpuInfo; - #ifdef _WIN64 - *pDllName = "amd_ags_x64.dll"; - #else - *pDllName = "amd_ags_x86.dll"; - #endif - } - else - { - *pDetectFn = NULL; - *pDllName = NULL; - return false; - } - - return true; -} +#define FF_EMPTY_GUID_STR L"{00000000-0000-0000-0000-000000000000}" +#define FF_GUID_STRLEN (sizeof(FF_EMPTY_GUID_STR) / sizeof(wchar_t) - 1) const char* ffDetectGPUImpl(FF_MAYBE_UNUSED const FFGPUOptions* options, FFlist* gpus) { - DISPLAY_DEVICEW displayDevice = { .cb = sizeof(displayDevice) }; - wchar_t regDirectxKey[MAX_PATH] = L"SOFTWARE\\Microsoft\\DirectX\\{"; - const uint32_t regDirectxKeyPrefixLength = (uint32_t) wcslen(regDirectxKey); - wchar_t regControlVideoKey[MAX_PATH] = L"SYSTEM\\CurrentControlSet\\Control\\Video\\{"; - const uint32_t regControlVideoKeyPrefixLength = (uint32_t) wcslen(regControlVideoKey); - const uint32_t deviceKeyPrefixLength = strlen("\\Registry\\Machine\\") + regControlVideoKeyPrefixLength; - - for (DWORD i = 0; EnumDisplayDevicesW(NULL, i, &displayDevice, 0); ++i) + HDEVINFO hdev __attribute__((__cleanup__(wrapSetupDiDestroyDeviceInfoList))) = + SetupDiGetClassDevsW(&GUID_DEVCLASS_DISPLAY, NULL, NULL, DIGCF_PRESENT); + + if(hdev == INVALID_HANDLE_VALUE) + return "SetupDiGetClassDevsW(&GUID_DEVCLASS_DISPLAY) failed"; + + wchar_t regDirectxKey[] = L"SOFTWARE\\Microsoft\\DirectX\\" FF_EMPTY_GUID_STR; + const uint32_t regDirectxKeyPrefixLength = (uint32_t) strlen("SOFTWARE\\Microsoft\\DirectX\\"); + wchar_t regControlVideoKey[] = L"SYSTEM\\CurrentControlSet\\Control\\Video\\" FF_EMPTY_GUID_STR L"\\0000"; + const uint32_t regControlVideoKeyPrefixLength = (uint32_t) strlen("SYSTEM\\CurrentControlSet\\Control\\Video\\"); + + SP_DEVINFO_DATA did = { .cbSize = sizeof(did) }; + for (DWORD idev = 0; SetupDiEnumDeviceInfo(hdev, idev, &did); ++idev) { - if (displayDevice.StateFlags & DISPLAY_DEVICE_MIRRORING_DRIVER) continue; + wchar_t hardwareId[256]; + if (!SetupDiGetDeviceRegistryPropertyW(hdev, &did, SPDRP_HARDWAREID, NULL, (PBYTE) hardwareId, sizeof(hardwareId), NULL)) + continue; - const uint32_t deviceKeyLength = (uint32_t) wcslen(displayDevice.DeviceKey); - if (__builtin_expect(deviceKeyLength == 100, true)) - { - if (wmemcmp(&displayDevice.DeviceKey[deviceKeyLength - 4], L"0000", 4) != 0) continue; - } - else + wchar_t videoId[39]; { - // DeviceKey can be empty. See #484 - FF_STRBUF_AUTO_DESTROY gpuName = ffStrbufCreateWS(displayDevice.DeviceString); - if (ffListContains(gpus, &gpuName, (void*) isGpuNameEqual)) continue; + FF_HKEY_AUTO_DESTROY hKey = SetupDiOpenDevRegKey(hdev, &did, DICS_FLAG_GLOBAL, 0, DIREG_DEV, KEY_QUERY_VALUE); + if (!hKey) continue; + DWORD videoIdLength = sizeof(videoId); + if (RegGetValueW(hKey, NULL, L"VideoID", RRF_RT_REG_SZ, NULL, videoId, &videoIdLength) != ERROR_SUCCESS || videoIdLength != (FF_GUID_STRLEN + 1) * sizeof(wchar_t)) + continue; } - // See: https://download.nvidia.com/XFree86/Linux-x86_64/545.23.06/README/supportedchips.html - // displayDevice.DeviceID = MatchingDeviceId "PCI\\VEN_10DE&DEV_2782&SUBSYS_513417AA&REV_A1" + wmemcpy(regControlVideoKey + regControlVideoKeyPrefixLength, videoId, FF_GUID_STRLEN); + FF_HKEY_AUTO_DESTROY hKey = NULL; + if (!ffRegOpenKeyForRead(HKEY_LOCAL_MACHINE, regControlVideoKey, &hKey, NULL)) continue; + unsigned vendorId = 0, deviceId = 0, subSystemId = 0, revId = 0; - swscanf(displayDevice.DeviceID, L"PCI\\VEN_%x&DEV_%x&SUBSYS_%x&REV_%x", &vendorId, &deviceId, &subSystemId, &revId); + swscanf(hardwareId, L"PCI\\VEN_%x&DEV_%x&SUBSYS_%x&REV_%x", &vendorId, &deviceId, &subSystemId, &revId); FFGPUResult* gpu = (FFGPUResult*)ffListAdd(gpus); ffStrbufInitStatic(&gpu->vendor, ffGetGPUVendorString(vendorId)); - ffStrbufInitWS(&gpu->name, displayDevice.DeviceString); + ffStrbufInit(&gpu->name); ffStrbufInit(&gpu->driver); ffStrbufInitStatic(&gpu->platformApi, "Direct3D"); gpu->temperature = FF_GPU_TEMP_UNSET; gpu->coreCount = FF_GPU_CORE_COUNT_UNSET; + gpu->coreUsage = FF_GPU_CORE_USAGE_UNSET; gpu->type = FF_GPU_TYPE_UNKNOWN; gpu->dedicated.total = gpu->dedicated.used = gpu->shared.total = gpu->shared.used = FF_GPU_VMEM_SIZE_UNSET; gpu->deviceId = 0; gpu->frequency = FF_GPU_FREQUENCY_UNSET; - if (deviceKeyLength == 100 && displayDevice.DeviceKey[deviceKeyPrefixLength - 1] == '{') + if (!ffRegReadStrbuf(hKey, L"DriverDesc", &gpu->name, NULL)) // faster, for some reason { - wmemcpy(regControlVideoKey + regControlVideoKeyPrefixLength, displayDevice.DeviceKey + deviceKeyPrefixLength, strlen("00000000-0000-0000-0000-000000000000}\\0000")); - FF_HKEY_AUTO_DESTROY hKey = NULL; - if (!ffRegOpenKeyForRead(HKEY_LOCAL_MACHINE, regControlVideoKey, &hKey, NULL)) continue; + wchar_t name[256]; + if (SetupDiGetDeviceRegistryPropertyW(hdev, &did, SPDRP_DEVICEDESC, NULL, (PBYTE) name, sizeof(name), NULL)) + ffStrbufSetWS(&gpu->name, name); + } + ffRegReadStrbuf(hKey, L"DriverVersion", &gpu->driver, NULL); - ffRegReadStrbuf(hKey, L"DriverVersion", &gpu->driver, NULL); + wmemcpy(regDirectxKey + regDirectxKeyPrefixLength, videoId, FF_GUID_STRLEN); + FF_HKEY_AUTO_DESTROY hDirectxKey = NULL; + if (ffRegOpenKeyForRead(HKEY_LOCAL_MACHINE, regDirectxKey, &hDirectxKey, NULL)) + { + uint64_t dedicatedVideoMemory = 0; + if(ffRegReadUint64(hDirectxKey, L"DedicatedVideoMemory", &dedicatedVideoMemory, NULL)) + gpu->type = dedicatedVideoMemory >= 1024 * 1024 * 1024 ? FF_GPU_TYPE_DISCRETE : FF_GPU_TYPE_INTEGRATED; - wmemcpy(regDirectxKey + regDirectxKeyPrefixLength, displayDevice.DeviceKey + deviceKeyPrefixLength, strlen("00000000-0000-0000-0000-000000000000}")); - FF_HKEY_AUTO_DESTROY hDirectxKey = NULL; - if (ffRegOpenKeyForRead(HKEY_LOCAL_MACHINE, regDirectxKey, &hDirectxKey, NULL)) - { - uint64_t dedicatedVideoMemory = 0; - if(ffRegReadUint64(hDirectxKey, L"DedicatedVideoMemory", &dedicatedVideoMemory, NULL)) - gpu->type = dedicatedVideoMemory >= 1024 * 1024 * 1024 ? FF_GPU_TYPE_DISCRETE : FF_GPU_TYPE_INTEGRATED; - - uint64_t dedicatedSystemMemory, sharedSystemMemory; - if(ffRegReadUint64(hDirectxKey, L"DedicatedSystemMemory", &dedicatedSystemMemory, NULL) && - ffRegReadUint64(hDirectxKey, L"SharedSystemMemory", &sharedSystemMemory, NULL)) - { - gpu->dedicated.total = dedicatedVideoMemory + dedicatedSystemMemory; - gpu->shared.total = sharedSystemMemory; - } - - ffRegReadUint64(hDirectxKey, L"AdapterLuid", &gpu->deviceId, NULL); - - uint32_t featureLevel = 0; - if(ffRegReadUint(hDirectxKey, L"MaxD3D12FeatureLevel", &featureLevel, NULL) && featureLevel) - ffStrbufSetF(&gpu->platformApi, "Direct3D 12.%u", (featureLevel & 0x0F00) >> 8); - else if(ffRegReadUint(hDirectxKey, L"MaxD3D11FeatureLevel", &featureLevel, NULL) && featureLevel) - ffStrbufSetF(&gpu->platformApi, "Direct3D 11.%u", (featureLevel & 0x0F00) >> 8); - } - else if (!ffRegReadUint64(hKey, L"HardwareInformation.qwMemorySize", &gpu->dedicated.total, NULL)) + uint64_t dedicatedSystemMemory, sharedSystemMemory; + if(ffRegReadUint64(hDirectxKey, L"DedicatedSystemMemory", &dedicatedSystemMemory, NULL) && + ffRegReadUint64(hDirectxKey, L"SharedSystemMemory", &sharedSystemMemory, NULL)) { - uint32_t vmem = 0; - if (ffRegReadUint(hKey, L"HardwareInformation.MemorySize", &vmem, NULL)) - gpu->dedicated.total = vmem; - gpu->type = gpu->dedicated.total > 1024 * 1024 * 1024 ? FF_GPU_TYPE_DISCRETE : FF_GPU_TYPE_INTEGRATED; + gpu->dedicated.total = dedicatedVideoMemory + dedicatedSystemMemory; + gpu->shared.total = sharedSystemMemory; } - if (gpu->vendor.length == 0) - { - ffRegReadStrbuf(hKey, L"ProviderName", &gpu->vendor, NULL); - if (ffStrbufContainS(&gpu->vendor, "Intel")) - ffStrbufSetStatic(&gpu->vendor, FF_GPU_VENDOR_NAME_INTEL); - else if (ffStrbufContainS(&gpu->vendor, "NVIDIA")) - ffStrbufSetStatic(&gpu->vendor, FF_GPU_VENDOR_NAME_NVIDIA); - else if (ffStrbufContainS(&gpu->vendor, "AMD") || ffStrbufContainS(&gpu->vendor, "ATI")) - ffStrbufSetStatic(&gpu->vendor, FF_GPU_VENDOR_NAME_AMD); - } + ffRegReadUint64(hDirectxKey, L"AdapterLuid", &gpu->deviceId, NULL); + + uint32_t featureLevel = 0; + if(ffRegReadUint(hDirectxKey, L"MaxD3D12FeatureLevel", &featureLevel, NULL) && featureLevel) + ffStrbufSetF(&gpu->platformApi, "Direct3D 12.%u", (featureLevel & 0x0F00) >> 8); + else if(ffRegReadUint(hDirectxKey, L"MaxD3D11FeatureLevel", &featureLevel, NULL) && featureLevel) + ffStrbufSetF(&gpu->platformApi, "Direct3D 11.%u", (featureLevel & 0x0F00) >> 8); + } + else if (!ffRegReadUint64(hKey, L"HardwareInformation.qwMemorySize", &gpu->dedicated.total, NULL)) + { + uint32_t vmem = 0; + if (ffRegReadUint(hKey, L"HardwareInformation.MemorySize", &vmem, NULL)) + gpu->dedicated.total = vmem; + gpu->type = gpu->dedicated.total > 1024 * 1024 * 1024 ? FF_GPU_TYPE_DISCRETE : FF_GPU_TYPE_INTEGRATED; + } + + if (gpu->vendor.length == 0) + { + ffRegReadStrbuf(hKey, L"ProviderName", &gpu->vendor, NULL); + if (ffStrbufContainS(&gpu->vendor, "Intel")) + ffStrbufSetStatic(&gpu->vendor, FF_GPU_VENDOR_NAME_INTEL); + else if (ffStrbufContainS(&gpu->vendor, "NVIDIA")) + ffStrbufSetStatic(&gpu->vendor, FF_GPU_VENDOR_NAME_NVIDIA); + else if (ffStrbufContainS(&gpu->vendor, "AMD") || ffStrbufContainS(&gpu->vendor, "ATI")) + ffStrbufSetStatic(&gpu->vendor, FF_GPU_VENDOR_NAME_AMD); } __typeof__(&ffDetectNvidiaGpuInfo) detectFn; @@ -144,23 +122,37 @@ const char* ffDetectGPUImpl(FF_MAYBE_UNUSED const FFGPUOptions* options, FFlist* if (getDriverSpecificDetectionFn(gpu->vendor.chars, &detectFn, &dllName) && (options->temp || options->driverSpecific)) { - if (vendorId && deviceId && subSystemId) + uint32_t pciBus, pciAddr; + if (SetupDiGetDeviceRegistryPropertyW(hdev, &did, SPDRP_BUSNUMBER, NULL, (PBYTE) &pciBus, sizeof(pciBus), NULL) && + SetupDiGetDeviceRegistryPropertyW(hdev, &did, SPDRP_ADDRESS, NULL, (PBYTE) &pciAddr, sizeof(pciAddr), NULL)) { + uint32_t pciDev = (pciAddr >> 16) & 0xFFFF; + uint32_t pciFunc = pciAddr & 0xFFFF; + detectFn( &(FFGpuDriverCondition) { - .type = FF_GPU_DRIVER_CONDITION_TYPE_DEVICE_ID | FF_GPU_DRIVER_CONDITION_TYPE_LUID, + .type = FF_GPU_DRIVER_CONDITION_TYPE_DEVICE_ID + | (gpu->deviceId > 0 ? FF_GPU_DRIVER_CONDITION_TYPE_LUID : 0) + | FF_GPU_DRIVER_CONDITION_TYPE_BUS_ID, .pciDeviceId = { .deviceId = deviceId, .vendorId = vendorId, .subSystemId = subSystemId, .revId = revId, }, + .pciBusId = { + .domain = 0, + .bus = pciBus, + .device = pciDev, + .func = pciFunc, + }, .luid = gpu->deviceId, }, (FFGpuDriverResult) { .temp = options->temp ? &gpu->temperature : NULL, .memory = options->driverSpecific ? &gpu->dedicated : NULL, .coreCount = options->driverSpecific ? (uint32_t*) &gpu->coreCount : NULL, + .coreUsage = options->driverSpecific ? &gpu->coreUsage : NULL, .type = &gpu->type, .frequency = options->driverSpecific ? &gpu->frequency : NULL, }, diff --git a/src/detection/gpu/gpu_wsl.cpp b/src/detection/gpu/gpu_wsl.cpp index 0b9c440d06..152b7dba08 100644 --- a/src/detection/gpu/gpu_wsl.cpp +++ b/src/detection/gpu/gpu_wsl.cpp @@ -71,8 +71,10 @@ const char* ffGPUDetectByDirectX(FF_MAYBE_UNUSED const FFGPUOptions* options, FF FFGPUResult* gpu = (FFGPUResult*) ffListAdd(gpus); ffStrbufInitS(&gpu->name, desc); gpu->coreCount = FF_GPU_CORE_COUNT_UNSET; + gpu->coreUsage = FF_GPU_CORE_USAGE_UNSET; gpu->temperature = FF_GPU_TEMP_UNSET; gpu->frequency = FF_GPU_FREQUENCY_UNSET; + gpu->deviceId = 0; ffStrbufInitStatic(&gpu->platformApi, "DXCore"); ffStrbufInit(&gpu->driver); @@ -119,8 +121,10 @@ const char* ffGPUDetectByDirectX(FF_MAYBE_UNUSED const FFGPUOptions* options, FF .temp = options->temp ? &gpu->temperature : NULL, .memory = options->driverSpecific ? &gpu->dedicated : NULL, .coreCount = options->driverSpecific ? (uint32_t*) &gpu->coreCount : NULL, + .coreUsage = options->driverSpecific ? &gpu->coreUsage : NULL, .type = &gpu->type, .frequency = options->driverSpecific ? &gpu->frequency : NULL, + .name = options->driverSpecific ? &gpu->name : NULL, }, "/usr/lib/wsl/lib/libnvidia-ml.so"); } } diff --git a/src/detection/gpu/mtml.h b/src/detection/gpu/mtml.h new file mode 100644 index 0000000000..be5a6cee5f --- /dev/null +++ b/src/detection/gpu/mtml.h @@ -0,0 +1,116 @@ +#pragma once + +// DISCLAIMER: +// THIS FILE IS CREATED FROM SCRATCH, BY READING THE OFFICIAL MTML API +// DOCUMENTATION REFERENCED BELOW, IN ORDER TO MAKE FASTFETCH MIT COMPLIANT. + +#define MTML_API __attribute__((visibility("default"))) +#define MTML_DEVICE_PCI_SBDF_BUFFER_SIZE 32 +#define MTML_DEVICE_NAME_BUFFER_SIZE 32 +#define MTML_DEVICE_UUID_BUFFER_SIZE 48 + +/** + * Return values for MTML API calls. + */ +typedef enum +{ + MTML_SUCCESS = 0, +} MtmlReturn; + +/** + * The brand of the device. + */ +typedef enum +{ + MTML_BRAND_MTT = 0, //!< MTT series. + MTML_BRAND_UNKNOWN, //!< An unknown brand. + + // Keep this on the last line. + MTML_BRAND_COUNT //!< The number of brands. +} MtmlBrandType; + +typedef struct MtmlLibrary MtmlLibrary; +typedef struct MtmlSystem MtmlSystem; +typedef struct MtmlDevice MtmlDevice; +typedef struct MtmlGpu MtmlGpu; +typedef struct MtmlMemory MtmlMemory; + +/** + * PCI information about a device. + */ +typedef struct +{ + char sbdf[MTML_DEVICE_PCI_SBDF_BUFFER_SIZE]; //!< The tuple segment:bus:device.function PCI identifier (& NULL terminator). + unsigned int segment; //!< The PCI segment group(domain) on which the device's bus resides, 0 to 0xffffffff. + unsigned int bus; //!< The bus on which the device resides, 0 to 0xff. + unsigned int device; //!< The device ID on the bus, 0 to 31. + unsigned int pciDeviceId; //!< The combined 16-bit device ID and 16-bit vendor ID. + unsigned int pciSubsystemId; //!< The 32-bit sub system device ID. + unsigned int busWidth; //!< @deprecated This value set to zero. + float pciMaxSpeed; //!< The maximum link speed (transfer rate per lane) of the device. The unit is GT/s. + float pciCurSpeed; //!< The current link speed (transfer rate per lane) of the device. The unit is GT/s. + unsigned int pciMaxWidth; //!< The maximum link width of the device. + unsigned int pciCurWidth; //!< The current link width of the device. + unsigned int pciMaxGen; //!< The maximum supported generation of the device. + unsigned int pciCurGen; //!< The current generation of the device. + int rsvd[6]; //!< Reserved for future extension. +} MtmlPciInfo; + +// Retrieves the brand of a device. +MtmlReturn MTML_API mtmlDeviceGetBrand(const MtmlDevice *dev, MtmlBrandType *type); +// Retrieves the index associated with the specified device. +MtmlReturn MTML_API mtmlDeviceGetIndex(const MtmlDevice *dev, unsigned int *index); +// Retrieves the name of a device. +MtmlReturn MTML_API mtmlDeviceGetName(const MtmlDevice *dev, char *name, unsigned int length); +// Retrieves the PCI attributes of a device. +MtmlReturn MTML_API mtmlDeviceGetPciInfo(const MtmlDevice *dev, MtmlPciInfo *pci); +/** + * Retrieves the UUID of a specified device. The UUID is a hexadecimal string in the + * form of xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx, where each 'x' is an ASCII character that represents a hexadecimal + * digit. The UUID is globally unique for every single device thus can be used to identify different devices + * physically. + */ +MtmlReturn MTML_API mtmlDeviceGetUUID(const MtmlDevice *dev, char *uuid, unsigned int length); +// Initializes a GPU opaque object to represent a specific graphic core on the target device that is designated by its index. +MtmlReturn MTML_API mtmlDeviceInitGpu(const MtmlDevice *dev, MtmlGpu **gpu); +// Initializes a memory opaque object to represent the memory on the target device. +MtmlReturn MTML_API mtmlDeviceInitMemory(const MtmlDevice *dev, MtmlMemory **mem); + +// Retrieves the maximum supported clock speed for the device's graphic core. +MtmlReturn MTML_API mtmlGpuGetMaxClock(const MtmlGpu *gpu, unsigned int *clockMhz); +// Retrieves the current temperature readings for the device's graphic core, in degrees Celsius. +MtmlReturn MTML_API mtmlGpuGetTemperature(const MtmlGpu *gpu, unsigned int *temp); +// Retrieves the current utilization rate for the device's graphic core. +MtmlReturn MTML_API mtmlGpuGetUtilization(const MtmlGpu *gpu, unsigned int *utilization); + +// Retrieves the number of devices that can be accessed by the library opaque object. +MtmlReturn MTML_API mtmlLibraryCountDevice(const MtmlLibrary *lib, unsigned int *count); +/** + * Initializes a device opaque object to represent a device that is designated by its index. + * The index ranges from (0) to (deviceCount - 1), where deviceCount is retrieved from \ref mtmlLibraryCountDevice(). + */ +MtmlReturn MTML_API mtmlLibraryInit(MtmlLibrary **lib); +/** + * Initializes a device opaque object to represent a device that is designated by its index. + * The index ranges from (0) to (deviceCount - 1), where deviceCount is retrieved from \ref mtmlLibraryCountDevice(). + */ +MtmlReturn MTML_API mtmlLibraryInitDeviceByIndex(const MtmlLibrary *lib, unsigned int index, MtmlDevice **dev); +/** + * Initializes a device opaque object to represent a device that is designated by its PCI Sbdf. + * The PCI Sbdf format like 00000000:3a:00.0 refer to \ref MtmlPciInfo::sbdf. + */ +MtmlReturn MTML_API mtmlLibraryInitDeviceByPciSbdf(const MtmlLibrary *lib, const char *pciSbdf, MtmlDevice **dev); +// Initializes a MtmlSystem opaque pointer that is bound to a library opaque object. +MtmlReturn MTML_API mtmlLibraryInitSystem(const MtmlLibrary *lib, MtmlSystem **sys); +/** + * Shuts down the library opaque object that is previously initialized by \ref mtmlLibraryInit() and releases its resources. + * The \a lib pointer cannot be used anymore after this function returns. + */ +MtmlReturn MTML_API mtmlLibraryShutDown(MtmlLibrary *lib); + +// Retrieves the amount of total memory available on the device, in bytes. +MtmlReturn MTML_API mtmlMemoryGetTotal(const MtmlMemory *mem, unsigned long long *total); +// Retrieves the amount of used memory on the device, in bytes. +MtmlReturn MTML_API mtmlMemoryGetUsed(const MtmlMemory *mem, unsigned long long *used); +// Retrieves the current memory utilization rate for the device. +MtmlReturn MTML_API mtmlMemoryGetUtilization(const MtmlMemory *mem, unsigned int *utilization); diff --git a/src/detection/gpu/nvml.h b/src/detection/gpu/nvml.h index ea776a4bee..fd10d651fe 100644 --- a/src/detection/gpu/nvml.h +++ b/src/detection/gpu/nvml.h @@ -7,6 +7,7 @@ // https://docs.nvidia.com/deploy/nvml-api/group__nvmlDeviceStructs.html #define NVML_DEVICE_PCI_BUS_ID_BUFFER_SIZE 32 #define NVML_DEVICE_PCI_BUS_ID_BUFFER_V2_SIZE 16 +#define NVML_DEVICE_NAME_V2_BUFFER_SIZE 96 typedef enum { NVML_SUCCESS = 0 } nvmlReturn_t; typedef struct nvmlDevice_t* nvmlDevice_t; @@ -94,6 +95,16 @@ typedef enum { NVML_BRAND_COUNT, } nvmlBrandType_t; +// https://docs.nvidia.com/deploy/nvml-api/structnvmlUtilization__t.html#structnvmlUtilization__t +// Utilization information for a device. +typedef struct +{ + // Percent of time over the past second during which one or more kernels was executing on the GPU + unsigned int gpu; + // Percent of time over the past second during which global (device) memory was being read or written + unsigned int memory; +} nvmlUtilization_t; + // https://docs.nvidia.com/deploy/nvml-api/group__nvmlInitializationAndCleanup.html#group__nvmlInitializationAndCleanup // Initialize NVML, but don't initialize any GPUs yet nvmlReturn_t nvmlInit_v2(void); @@ -119,3 +130,7 @@ extern nvmlReturn_t nvmlDeviceGetNumGpuCores(nvmlDevice_t device, unsigned int* extern nvmlReturn_t nvmlDeviceGetMaxClockInfo(nvmlDevice_t device, nvmlClockType_t type, unsigned int* clock); // Retrieves the brand of this device extern nvmlReturn_t nvmlDeviceGetBrand(nvmlDevice_t device, nvmlBrandType_t* type); +// Retrieves the current utilization rates for the device +extern nvmlReturn_t nvmlDeviceGetUtilizationRates(nvmlDevice_t device, nvmlUtilization_t *utilization); +// Retrieves the name of this device. +extern nvmlReturn_t nvmlDeviceGetName(nvmlDevice_t device, char *name, unsigned int length); diff --git a/src/detection/media/media_linux.c b/src/detection/media/media_linux.c index 5dd673338d..83949bcdff 100644 --- a/src/detection/media/media_linux.c +++ b/src/detection/media/media_linux.c @@ -11,6 +11,13 @@ #include "common/dbus.h" #include "common/library.h" +#define FF_DBUS_ITER_CONTINUE(dbus, iterator) \ + { \ + if(!(dbus)->lib->ffdbus_message_iter_next(iterator)) \ + break; \ + continue; \ + } + static bool getBusProperties(FFDBusData* data, const char* busName, FFMediaResult* result) { DBusMessage* reply = ffDBusGetProperty(data, busName, "/org/mpris/MediaPlayer2", "org.mpris.MediaPlayer2.Player", "Metadata"); @@ -61,13 +68,13 @@ static bool getBusProperties(FFDBusData* data, const char* busName, FFMediaResul data->lib->ffdbus_message_iter_next(&dictIterator); if(ffStrEquals(key, "xesam:title")) - ffDBusGetValue(data, &dictIterator, &result->song); + ffDBusGetString(data, &dictIterator, &result->song); else if(ffStrEquals(key, "xesam:album")) - ffDBusGetValue(data, &dictIterator, &result->album); + ffDBusGetString(data, &dictIterator, &result->album); else if(ffStrEquals(key, "xesam:artist")) - ffDBusGetValue(data, &dictIterator, &result->artist); + ffDBusGetString(data, &dictIterator, &result->artist); else if(ffStrEquals(key, "xesam:url")) - ffDBusGetValue(data, &dictIterator, &result->url); + ffDBusGetString(data, &dictIterator, &result->url); if(result->song.length > 0 && result->artist.length > 0 && result->album.length > 0 && result->url.length > 0) break; @@ -85,6 +92,8 @@ static bool getBusProperties(FFDBusData* data, const char* busName, FFMediaResul return false; } + ffDBusGetPropertyString(data, busName, "/org/mpris/MediaPlayer2", "org.mpris.MediaPlayer2.Player", "PlaybackStatus", &result->status); + //Set short bus name ffStrbufAppendS(&result->playerId, busName + sizeof(FF_DBUS_MPRIS_PREFIX) - 1); @@ -119,7 +128,7 @@ static void getBestBus(FFDBusData* data, FFMediaResult* result) getBusProperties(data, FF_DBUS_MPRIS_PREFIX"plasma-browser-integration", result) ) return; - DBusMessage* reply = ffDBusGetMethodReply(data, "org.freedesktop.DBus", "/org/freedesktop/DBus", "org.freedesktop.DBus", "ListNames"); + DBusMessage* reply = ffDBusGetMethodReply(data, "org.freedesktop.DBus", "/org/freedesktop/DBus", "org.freedesktop.DBus", "ListNames", NULL); if(reply == NULL) return; diff --git a/src/detection/opencl/opencl.c b/src/detection/opencl/opencl.c index 462f3aff20..7da61a23ad 100644 --- a/src/detection/opencl/opencl.c +++ b/src/detection/opencl/opencl.c @@ -81,6 +81,7 @@ static const char* openCLHandleData(OpenCLData* data, FFOpenCLResult* result) gpu->dedicated.total = gpu->dedicated.used = gpu->shared.total = gpu->shared.used = FF_GPU_VMEM_SIZE_UNSET; gpu->deviceId = (size_t) deviceID; gpu->frequency = FF_GPU_FREQUENCY_UNSET; + gpu->coreUsage = FF_GPU_CORE_USAGE_UNSET; if (data->ffclGetDeviceInfo(deviceID, CL_DEVICE_VERSION, sizeof(buffer), buffer, NULL) == CL_SUCCESS) { diff --git a/src/detection/physicalmemory/physicalmemory.c b/src/detection/physicalmemory/physicalmemory.c index 6591c32453..c5df181f38 100644 --- a/src/detection/physicalmemory/physicalmemory.c +++ b/src/detection/physicalmemory/physicalmemory.c @@ -23,6 +23,12 @@ static inline const char* getVendorString(unsigned vendorId) void FFPhysicalMemoryUpdateVendorString(FFPhysicalMemoryResult* device) { + if (ffStrbufEqualS(&device->vendor, "Unknown")) + { + ffStrbufClear(&device->vendor); + return; + } + char vendorIdStr[5]; if (ffStrbufStartsWithS(&device->vendor, "0x")) { diff --git a/src/detection/physicalmemory/physicalmemory_linux.c b/src/detection/physicalmemory/physicalmemory_linux.c index 1eb971e8ba..fc83f92a31 100644 --- a/src/detection/physicalmemory/physicalmemory_linux.c +++ b/src/detection/physicalmemory/physicalmemory_linux.c @@ -75,10 +75,10 @@ const char* ffDetectPhysicalMemory(FFlist* result) if (!data) return "Memory device is not found in SMBIOS data"; - for (; data->Header.Type == FF_SMBIOS_TYPE_MEMORY_DEVICE; - data = (const FFSmbiosMemoryDevice*) ffSmbiosNextEntry(&data->Header)) + for (; data->Header.Type < FF_SMBIOS_TYPE_END_OF_TABLE; + data = (const FFSmbiosMemoryDevice*) ffSmbiosNextEntry(&data->Header)) { - if (data->Size == 0) continue; + if (data->Header.Type != FF_SMBIOS_TYPE_MEMORY_DEVICE || data->Size == 0) continue; const char* strings = (const char*) data + data->Header.Length; diff --git a/src/detection/terminalshell/terminalshell.c b/src/detection/terminalshell/terminalshell.c index 230b515c18..9d65bde3aa 100644 --- a/src/detection/terminalshell/terminalshell.c +++ b/src/detection/terminalshell/terminalshell.c @@ -200,28 +200,6 @@ static bool getShellVersionWinPowerShell(FFstrbuf* exe, FFstrbuf* version) NULL }) == NULL; } -#else -static bool getShellVersionGeneric(FFstrbuf* exe, const char* exeName, FFstrbuf* version) -{ - FF_STRBUF_AUTO_DESTROY command = ffStrbufCreate(); - ffStrbufAppendS(&command, "printf \"%s\" \"$"); - ffStrbufAppendTransformS(&command, exeName, toupper); - ffStrbufAppendS(&command, "_VERSION\""); - - if (ffProcessAppendStdOut(version, (char* const[]) { - "env", - "-i", - exe->chars, - "-c", - command.chars, - NULL - }) != NULL) - return false; - - ffStrbufSubstrBeforeFirstC(version, '('); - ffStrbufRemoveStrings(version, 2, (const char*[]) { "-release", "release" }); - return true; -} #endif bool fftsGetShellVersion(FFstrbuf* exe, const char* exeName, FFstrbuf* version) @@ -261,9 +239,9 @@ bool fftsGetShellVersion(FFstrbuf* exe, const char* exeName, FFstrbuf* version) return getShellVersionWinPowerShell(exe, version); return getFileVersion(exe->chars, version); - #else - return getShellVersionGeneric(exe, exeName, version); #endif + + return false; } FF_MAYBE_UNUSED static bool getTerminalVersionTermux(FFstrbuf* version) diff --git a/src/detection/terminalshell/terminalshell_linux.c b/src/detection/terminalshell/terminalshell_linux.c index 61c05a82bb..30e1319040 100644 --- a/src/detection/terminalshell/terminalshell_linux.c +++ b/src/detection/terminalshell/terminalshell_linux.c @@ -42,7 +42,6 @@ static pid_t getShellInfo(FFShellResult* result, pid_t pid) ffStrbufEqualS(&result->processName, "sudo") || ffStrbufEqualS(&result->processName, "su") || ffStrbufEqualS(&result->processName, "strace") || - ffStrbufEqualS(&result->processName, "sshd") || ffStrbufEqualS(&result->processName, "gdb") || ffStrbufEqualS(&result->processName, "lldb") || ffStrbufEqualS(&result->processName, "lldb-mi") || @@ -51,7 +50,7 @@ static pid_t getShellInfo(FFShellResult* result, pid_t pid) ffStrbufEqualS(&result->processName, "perf") || ffStrbufEqualS(&result->processName, "guake-wrapped") || ffStrbufEqualS(&result->processName, "time") || - ffStrbufEqualS(&result->processName, "hyfetch") || //when hyfetch uses fastfetch as backend + ffStrbufContainS(&result->processName, "hyfetch") || //when hyfetch uses fastfetch as backend ffStrbufEqualS(&result->processName, "clifm") || //https://github.com/leo-arch/clifm/issues/289 ffStrbufEqualS(&result->processName, "valgrind") || ffStrbufEqualS(&result->processName, "fastfetch") || //994 @@ -102,7 +101,6 @@ static pid_t getTerminalInfo(FFTerminalResult* result, pid_t pid) ffStrbufEqualS(&result->processName, "oil.ovm") || ffStrbufEqualS(&result->processName, "xonsh") || // works in Linux but not in macOS because kernel returns `Python` in this case ffStrbufEqualS(&result->processName, "login") || - ffStrbufEqualS(&result->processName, "sshd") || ffStrbufEqualS(&result->processName, "clifm") || // https://github.com/leo-arch/clifm/issues/289 ffStrbufEqualS(&result->processName, "chezmoi") || // #762 #ifdef __linux__ @@ -314,6 +312,8 @@ static void setTerminalInfoDetails(FFTerminalResult* result) ffStrbufInitStatic(&result->prettyName, "tmux"); else if(ffStrbufStartsWithS(&result->processName, "screen-")) ffStrbufInitStatic(&result->prettyName, "screen"); + else if(ffStrbufEqualS(&result->processName, "sshd") || ffStrbufStartsWithS(&result->processName, "sshd-")) + ffStrbufInitCopy(&result->prettyName, &result->tty); #if defined(__ANDROID__) diff --git a/src/detection/terminalshell/terminalshell_windows.c b/src/detection/terminalshell/terminalshell_windows.c index 4f8e02f572..f1e27d63e2 100644 --- a/src/detection/terminalshell/terminalshell_windows.c +++ b/src/detection/terminalshell/terminalshell_windows.c @@ -53,7 +53,6 @@ static uint32_t getShellInfo(FFShellResult* result, uint32_t pid) if( ffStrbufIgnCaseEqualS(&result->prettyName, "sudo") || ffStrbufIgnCaseEqualS(&result->prettyName, "su") || - ffStrbufIgnCaseEqualS(&result->prettyName, "sshd") || ffStrbufIgnCaseEqualS(&result->prettyName, "gdb") || ffStrbufIgnCaseEqualS(&result->prettyName, "lldb") || ffStrbufIgnCaseEqualS(&result->prettyName, "python") || // python on windows generates shim executables @@ -155,7 +154,7 @@ static bool getTerminalFromEnv(FFTerminalResult* result) } //SSH - if(getenv("SSH_CONNECTION") != NULL) + if(getenv("SSH_TTY") != NULL) term = getenv("SSH_TTY"); //Windows Terminal @@ -315,6 +314,11 @@ static void setTerminalInfoDetails(FFTerminalResult* result) ffStrbufSetStatic(&result->prettyName, "Windows Explorer"); else if(ffStrbufEqualS(&result->prettyName, "wezterm-gui")) ffStrbufSetStatic(&result->prettyName, "WezTerm"); + else if(ffStrbufIgnCaseEqualS(&result->prettyName, "sshd") || ffStrbufStartsWithIgnCaseS(&result->prettyName, "sshd-")) + { + const char* tty = getenv("SSH_TTY"); + if (tty) ffStrbufSetS(&result->prettyName, tty); + } } bool fftsGetTerminalVersion(FFstrbuf* processName, FFstrbuf* exe, FFstrbuf* version); diff --git a/src/detection/users/users_linux.c b/src/detection/users/users_linux.c index 86e69dbe42..30c38c2af1 100644 --- a/src/detection/users/users_linux.c +++ b/src/detection/users/users_linux.c @@ -1,7 +1,7 @@ #include "fastfetch.h" #include "users.h" -#if __has_include() +#if FF_HAVE_UTMPX #include #else //for Android compatibility diff --git a/src/detection/vulkan/vulkan.c b/src/detection/vulkan/vulkan.c index e71cfe18f1..dc3b42089e 100644 --- a/src/detection/vulkan/vulkan.c +++ b/src/detection/vulkan/vulkan.c @@ -237,6 +237,7 @@ static const char* detectVulkan(FFVulkanResult* result) gpu->coreCount = FF_GPU_CORE_COUNT_UNSET; gpu->temperature = FF_GPU_TEMP_UNSET; gpu->frequency = FF_GPU_FREQUENCY_UNSET; + gpu->coreUsage = FF_GPU_CORE_USAGE_UNSET; next: continue; diff --git a/src/detection/wifi/wifi.h b/src/detection/wifi/wifi.h index 2e292439d4..10b4ffdbc9 100644 --- a/src/detection/wifi/wifi.h +++ b/src/detection/wifi/wifi.h @@ -12,7 +12,7 @@ struct FFWifiConnection { FFstrbuf status; FFstrbuf ssid; - FFstrbuf macAddress; + FFstrbuf bssid; FFstrbuf protocol; FFstrbuf security; double signalQuality; // Percentage diff --git a/src/detection/wifi/wifi_android.c b/src/detection/wifi/wifi_android.c index e34f45365b..a149892422 100644 --- a/src/detection/wifi/wifi_android.c +++ b/src/detection/wifi/wifi_android.c @@ -37,7 +37,7 @@ const char* ffDetectWifi(FFlist* result) ffStrbufInit(&item->inf.status); ffStrbufInit(&item->conn.status); ffStrbufInit(&item->conn.ssid); - ffStrbufInit(&item->conn.macAddress); + ffStrbufInit(&item->conn.bssid); ffStrbufInit(&item->conn.protocol); ffStrbufInit(&item->conn.security); item->conn.signalQuality = 0.0/0.0; @@ -53,12 +53,12 @@ const char* ffDetectWifi(FFlist* result) if(!ffStrbufEqualS(&item->inf.status, "COMPLETED")) return NULL; - + double rssi = yyjson_get_num(yyjson_obj_get(root, "rssi")); item->conn.signalQuality = rssi >= -50 ? 100 : rssi <= -100 ? 0 : (rssi + 100) * 2; - + ffStrbufAppendS(&item->inf.description, yyjson_get_str(yyjson_obj_get(root, "ip"))); - ffStrbufAppendS(&item->conn.macAddress, yyjson_get_str(yyjson_obj_get(root, "bssid"))); + ffStrbufAppendS(&item->conn.bssid, yyjson_get_str(yyjson_obj_get(root, "bssid"))); ffStrbufAppendS(&item->conn.ssid, yyjson_get_str(yyjson_obj_get(root, "ssid"))); return NULL; diff --git a/src/detection/wifi/wifi_apple.m b/src/detection/wifi/wifi_apple.m index 8fdb406370..55085fda18 100644 --- a/src/detection/wifi/wifi_apple.m +++ b/src/detection/wifi/wifi_apple.m @@ -6,21 +6,17 @@ struct Apple80211; // https://code.google.com/archive/p/iphone-wireless/wikis/Apple80211.wiki // 0 is successful; < 0 is failure -int Apple80211Open(struct Apple80211 **handle) __attribute__((weak_import)); -int Apple80211BindToInterface(struct Apple80211 *handle, CFStringRef interface) __attribute__((weak_import)); int Apple80211GetInfoCopy(struct Apple80211 *handle, CFDictionaryRef *info) __attribute__((weak_import)); -int Apple80211Close(struct Apple80211 *handle) __attribute__((weak_import)); -static NSDictionary* getWifiInfoByApple80211(NSString* ifName) -{ - if (!Apple80211Open || !Apple80211BindToInterface || !Apple80211GetInfoCopy || !Apple80211Close) return NULL; +@interface CWInterface() +@property (readonly) struct Apple80211* device; +@end - struct Apple80211* handle = NULL; - if (Apple80211Open(&handle) < 0) return NULL; - if (Apple80211BindToInterface(handle, (__bridge CFStringRef)ifName) < 0) return NULL; - CFDictionaryRef result; - if (Apple80211GetInfoCopy(handle, &result) < 0) return NULL; - Apple80211Close(handle); +static NSDictionary* getWifiInfoByApple80211(CWInterface* inf) +{ + if (!Apple80211GetInfoCopy) return NULL; + CFDictionaryRef result = NULL; + if (Apple80211GetInfoCopy(inf.device, &result) < 0) return NULL; return CFBridgingRelease(result); } @@ -37,7 +33,7 @@ ffStrbufInit(&item->inf.status); ffStrbufInit(&item->conn.status); ffStrbufInit(&item->conn.ssid); - ffStrbufInit(&item->conn.macAddress); + ffStrbufInit(&item->conn.bssid); ffStrbufInit(&item->conn.protocol); ffStrbufInit(&item->conn.security); item->conn.signalQuality = 0.0/0.0; @@ -57,15 +53,15 @@ if (inf.ssid) // https://developer.apple.com/forums/thread/732431 ffStrbufAppendS(&item->conn.ssid, inf.ssid.UTF8String); - else if (apple || (apple = getWifiInfoByApple80211(inf.interfaceName))) + else if (apple || (apple = getWifiInfoByApple80211(inf))) ffStrbufAppendS(&item->conn.ssid, [[apple valueForKey:@"SSID_STR"] UTF8String]); else ffStrbufSetStatic(&item->conn.ssid, ""); // https://developer.apple.com/forums/thread/732431 if (inf.bssid) - ffStrbufAppendS(&item->conn.macAddress, inf.bssid.UTF8String); - else if (apple || (apple = getWifiInfoByApple80211(inf.interfaceName))) - ffStrbufAppendS(&item->conn.macAddress, [[apple valueForKey:@"BSSID"] UTF8String]); + ffStrbufAppendS(&item->conn.bssid, inf.bssid.UTF8String); + else if (apple || (apple = getWifiInfoByApple80211(inf))) + ffStrbufAppendS(&item->conn.bssid, [[apple valueForKey:@"BSSID"] UTF8String]); switch(inf.activePHYMode) { @@ -152,7 +148,7 @@ break; case kCWSecurityUnknown: // Sonoma... - if (apple || (apple = getWifiInfoByApple80211(inf.interfaceName))) + if (apple || (apple = getWifiInfoByApple80211(inf))) { NSDictionary* authType = [apple valueForKey:@"AUTH_TYPE"]; if (authType) diff --git a/src/detection/wifi/wifi_bsd.c b/src/detection/wifi/wifi_bsd.c index 66edc10c6f..1f47cf9ef3 100644 --- a/src/detection/wifi/wifi_bsd.c +++ b/src/detection/wifi/wifi_bsd.c @@ -28,7 +28,7 @@ const char* ffDetectWifi(FFlist* result) ffStrbufInit(&item->inf.status); ffStrbufInit(&item->conn.status); ffStrbufInit(&item->conn.ssid); - ffStrbufInit(&item->conn.macAddress); + ffStrbufInit(&item->conn.bssid); ffStrbufInit(&item->conn.protocol); ffStrbufInit(&item->conn.security); item->conn.signalQuality = 0.0/0.0; @@ -42,7 +42,7 @@ const char* ffDetectWifi(FFlist* result) if (ibssid < item->conn.ssid.length) { ibssid += (uint32_t) strlen(" bssid "); - ffStrbufSetS(&item->conn.macAddress, item->conn.ssid.chars + ibssid); + ffStrbufSetS(&item->conn.bssid, item->conn.ssid.chars + ibssid); } ffStrbufSubstrBeforeFirstC(&item->conn.ssid, ' '); diff --git a/src/detection/wifi/wifi_linux.c b/src/detection/wifi/wifi_linux.c index 0392fb706f..1848c236d9 100644 --- a/src/detection/wifi/wifi_linux.c +++ b/src/detection/wifi/wifi_linux.c @@ -1,184 +1,95 @@ #include "wifi.h" -#include "util/stringUtils.h" - -#include -#include - -#ifdef FF_HAVE_LIBNM - +#include "common/dbus.h" #include "common/processing.h" #include "common/properties.h" -#include "common/library.h" - -#include -#define NM_NO_INCLUDE_EXTRA_HEADERS 1 -#include +#include "util/stringUtils.h" -//https://developer-old.gnome.org/libnm/stable/libnm-nm-dbus-interface.html -#ifndef NM_VERSION_1_26 - #define NM_802_11_AP_SEC_KEY_MGMT_OWE 0x00000800 - #define NM_802_11_AP_SEC_KEY_MGMT_OWE_TM 0x00001000 -#endif +#include -static const char* detectWifiWithLibnm(FFlist* result) +#ifdef FF_HAVE_DBUS +// https://people.freedesktop.org/~lkundrak/nm-docs/nm-dbus-types.html#NM80211ApFlags +typedef enum { + NM_802_11_AP_FLAGS_NONE = 0x00000000, + NM_802_11_AP_FLAGS_PRIVACY = 0x00000001, + NM_802_11_AP_FLAGS_WPS = 0x00000002, + NM_802_11_AP_FLAGS_WPS_PBC = 0x00000004, + NM_802_11_AP_FLAGS_WPS_PIN = 0x00000008, +} NM80211ApFlags; + +// https://people.freedesktop.org/~lkundrak/nm-docs/nm-dbus-types.html#NM80211ApSecurityFlags +typedef enum { + NM_802_11_AP_SEC_NONE = 0x00000000, + NM_802_11_AP_SEC_PAIR_WEP40 = 0x00000001, + NM_802_11_AP_SEC_PAIR_WEP104 = 0x00000002, + NM_802_11_AP_SEC_PAIR_TKIP = 0x00000004, + NM_802_11_AP_SEC_PAIR_CCMP = 0x00000008, + NM_802_11_AP_SEC_GROUP_WEP40 = 0x00000010, + NM_802_11_AP_SEC_GROUP_WEP104 = 0x00000020, + NM_802_11_AP_SEC_GROUP_TKIP = 0x00000040, + NM_802_11_AP_SEC_GROUP_CCMP = 0x00000080, + NM_802_11_AP_SEC_KEY_MGMT_PSK = 0x00000100, + NM_802_11_AP_SEC_KEY_MGMT_802_1X = 0x00000200, + NM_802_11_AP_SEC_KEY_MGMT_SAE = 0x00000400, + NM_802_11_AP_SEC_KEY_MGMT_OWE = 0x00000800, + NM_802_11_AP_SEC_KEY_MGMT_OWE_TM = 0x00001000, + NM_802_11_AP_SEC_KEY_MGMT_EAP_SUITE_B_192 = 0x00002000, +} NM80211ApSecurityFlags; + +static const char* detectWifiWithNm(FFWifiResult* item, FFstrbuf* buffer) { - FF_LIBRARY_LOAD(nm, &instance.config.library.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) - 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 type = ffnm_device_wifi_get_type(); - gboolean return_value; - if (!inst) - return_value = FALSE; - else if (inst->g_class && inst->g_class->g_type == type) - return_value = TRUE; - else - return_value = ffg_type_check_instance_is_a (inst, type); - return_value; - }) //NM_IS_DEVICE_WIFI(device) - ) - continue; + FFDBusData dbus; + const char* error = ffDBusLoadData(DBUS_BUS_SYSTEM, &dbus); + if(error) + return error; - 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; + { + DBusMessage* device = ffDBusGetMethodReply(&dbus, "org.freedesktop.NetworkManager", "/org/freedesktop/NetworkManager", "org.freedesktop.NetworkManager", "GetDeviceByIpIface", item->inf.description.chars); + if(!device) + return "Failed to call GetDeviceByIpIface"; - ffStrbufAppendS(&item->inf.description, ffnm_device_get_iface(device)); - NMDeviceState state = ffnm_device_get_state(device); - switch(state) + ffStrbufClear(buffer); + DBusMessageIter rootIter; + if(!dbus.lib->ffdbus_message_iter_init(device, &rootIter) || !ffDBusGetString(&dbus, &rootIter, buffer)) { - case NM_DEVICE_STATE_UNKNOWN: - ffStrbufSetStatic(&item->inf.status, "Unknown"); - break; - case NM_DEVICE_STATE_UNMANAGED: - ffStrbufSetStatic(&item->inf.status, "Unmanaged"); - break; - case NM_DEVICE_STATE_UNAVAILABLE: - ffStrbufSetStatic(&item->inf.status, "Unavailable"); - break; - case NM_DEVICE_STATE_DISCONNECTED: - ffStrbufSetStatic(&item->inf.status, "Disconnected"); - break; - case NM_DEVICE_STATE_PREPARE: - ffStrbufSetStatic(&item->inf.status, "Prepare"); - break; - case NM_DEVICE_STATE_CONFIG: - ffStrbufSetStatic(&item->inf.status, "Config"); - break; - case NM_DEVICE_STATE_NEED_AUTH: - ffStrbufSetStatic(&item->inf.status, "Need auth"); - break; - case NM_DEVICE_STATE_IP_CONFIG: - ffStrbufSetStatic(&item->inf.status, "IP config"); - break; - case NM_DEVICE_STATE_IP_CHECK: - ffStrbufSetStatic(&item->inf.status, "IP check"); - break; - case NM_DEVICE_STATE_SECONDARIES: - ffStrbufSetStatic(&item->inf.status, "Secondaries"); - break; - case NM_DEVICE_STATE_ACTIVATED: - ffStrbufSetStatic(&item->inf.status, "Activated"); - break; - case NM_DEVICE_STATE_DEACTIVATING: - ffStrbufSetStatic(&item->inf.status, "Deactivating"); - break; - case NM_DEVICE_STATE_FAILED: - ffStrbufSetStatic(&item->inf.status, "Failed"); - break; + dbus.lib->ffdbus_message_unref(device); + return "Failed to get device path"; } - if(state != NM_DEVICE_STATE_ACTIVATED) - continue; + dbus.lib->ffdbus_message_unref(device); + } - NMAccessPoint* ap = ffnm_device_wifi_get_active_access_point((NMDeviceWifi *) device); - if (!ap) - continue; + if (item->conn.txRate != item->conn.txRate) + { + uint32_t bitrate; + if (ffDBusGetPropertyUint(&dbus, "org.freedesktop.NetworkManager", buffer->chars, "org.freedesktop.NetworkManager.Device.Wireless", "Bitrate", &bitrate)) + item->conn.txRate = bitrate / 1000.; + } - ffStrbufSetStatic(&item->conn.status, "Connected"); + FF_STRBUF_AUTO_DESTROY apPath = ffStrbufCreate(); + if (!ffDBusGetPropertyString(&dbus, "org.freedesktop.NetworkManager", buffer->chars, "org.freedesktop.NetworkManager.Device.Wireless", "ActiveAccessPoint", &apPath)) + return "Failed to get active access point path"; - 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); - } + if (!item->conn.status.length) + ffStrbufSetStatic(&item->conn.status, "connected"); - 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); - - FF_STRBUF_AUTO_DESTROY output = ffStrbufCreate(); - if(!ffProcessAppendStdOut(&output, (char* const[]){ - "iw", - "dev", - item->inf.description.chars, - "link", - NULL - })) - { - if(ffParsePropLines(output.chars, "rx bitrate: ", &item->conn.protocol)) - { - sscanf(item->conn.protocol.chars, "%lf", &item->conn.rxRate); - } - if(ffParsePropLines(output.chars, "tx bitrate: ", &item->conn.protocol)) - { - if(ffStrbufContainS(&item->conn.protocol, " HE-MCS ")) - ffStrbufSetStatic(&item->conn.protocol, "802.11ax (Wi-Fi 6)"); - else if(ffStrbufContainS(&item->conn.protocol, " VHT-MCS ")) - ffStrbufSetStatic(&item->conn.protocol, "802.11ac (Wi-Fi 5)"); - else if(ffStrbufContainS(&item->conn.protocol, " MCS ")) - ffStrbufSetStatic(&item->conn.protocol, "802.11n (Wi-Fi 4)"); - else - ffStrbufClear(&item->conn.protocol); - - sscanf(item->conn.protocol.chars, "%lf", &item->conn.txRate); - } - } + if (!item->conn.ssid.length) + ffDBusGetPropertyString(&dbus, "org.freedesktop.NetworkManager", apPath.chars, "org.freedesktop.NetworkManager.AccessPoint", "Ssid", &item->conn.ssid); - 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 (!item->conn.bssid.length) + ffDBusGetPropertyString(&dbus, "org.freedesktop.NetworkManager", apPath.chars, "org.freedesktop.NetworkManager.AccessPoint", "HwAddress", &item->conn.bssid); + + if (item->conn.signalQuality != item->conn.signalQuality) + { + uint32_t strengthPercent; + if (ffDBusGetPropertyUint(&dbus, "org.freedesktop.NetworkManager", apPath.chars, "org.freedesktop.NetworkManager.AccessPoint", "Strength", &strengthPercent)) + item->conn.signalQuality = strengthPercent; + } + NM80211ApFlags flags; + NM80211ApSecurityFlags wpaFlags, rsnFlags; + if (ffDBusGetPropertyUint(&dbus, "org.freedesktop.NetworkManager", apPath.chars, "org.freedesktop.NetworkManager.AccessPoint", "Flags", &flags) && + ffDBusGetPropertyUint(&dbus, "org.freedesktop.NetworkManager", apPath.chars, "org.freedesktop.NetworkManager.AccessPoint", "WpaFlags", &wpaFlags) && + ffDBusGetPropertyUint(&dbus, "org.freedesktop.NetworkManager", apPath.chars , "org.freedesktop.NetworkManager.AccessPoint", "RsnFlags", &rsnFlags)) + { 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/"); @@ -205,36 +116,167 @@ static const char* detectWifiWithLibnm(FFlist* result) ffStrbufTrimRight(&item->conn.security, '/'); } - ffg_object_unref(client); - return NULL; } +#endif // FF_HAVE_DBUS + +static const char* detectWifiWithIw(FFWifiResult* item, FFstrbuf* buffer) +{ + const char* error = NULL; + FF_STRBUF_AUTO_DESTROY output = ffStrbufCreate(); + if((error = ffProcessAppendStdOut(&output, (char* const[]){ + "iw", + "dev", + item->inf.description.chars, + "link", + NULL + }))) + return error; + + if(output.length == 0) + return "iw command execution failed"; + + if(!ffParsePropLines(output.chars, "Connected to ", &item->conn.bssid)) + { + ffStrbufAppendS(&item->conn.status, "disconnected"); + return NULL; + } + + ffStrbufAppendS(&item->conn.status, "connected"); + ffStrbufSubstrBeforeFirstC(&item->conn.bssid, ' '); + ffStrbufUpperCase(&item->conn.bssid); + + ffParsePropLines(output.chars, "SSID: ", &item->conn.ssid); + + ffStrbufClear(buffer); + if(ffParsePropLines(output.chars, "signal: ", buffer)) + { + int level = (int) ffStrbufToSInt(buffer, INT_MAX); + if (level != INT_MAX) + item->conn.signalQuality = level >= -50 ? 100 : level <= -100 ? 0 : (level + 100) * 2; + } + + ffStrbufClear(buffer); + if(ffParsePropLines(output.chars, "rx bitrate: ", buffer)) + item->conn.rxRate = ffStrbufToDouble(buffer); -#endif + ffStrbufClear(buffer); + if(ffParsePropLines(output.chars, "tx bitrate: ", buffer)) + { + item->conn.txRate = ffStrbufToDouble(buffer); + + if(ffStrbufContainS(buffer, " HE-MCS ")) + ffStrbufSetStatic(&item->conn.protocol, "802.11ax (Wi-Fi 6)"); + else if(ffStrbufContainS(buffer, " VHT-MCS ")) + ffStrbufSetStatic(&item->conn.protocol, "802.11ac (Wi-Fi 5)"); + else if(ffStrbufContainS(buffer, " MCS ")) + ffStrbufSetStatic(&item->conn.protocol, "802.11n (Wi-Fi 4)"); + } -#if __has_include() -#define FF_DETECT_WIFI_WITH_IOCTLS + return NULL; +} +#if FF_HAVE_LINUX_WIRELESS #include "common/io/io.h" -#include #include #include #include -#include //TODO: Don't depend on kernel headers +#include -static const char* detectWifiWithIoctls(FFlist* result) +static const char* detectWifiWithIoctls(FFWifiResult* item) +{ + FF_AUTO_CLOSE_FD int sock = socket(AF_INET, SOCK_DGRAM | SOCK_CLOEXEC, 0); + if(sock < 0) + return "socket() failed"; + + struct iwreq iwr; + strncpy(iwr.ifr_name, item->inf.description.chars, 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) + { + ffStrbufSetStatic(&item->conn.status, "connected"); + ffStrbufRecalculateLength(&item->conn.ssid); + } + + if(ioctl(sock, SIOCGIWNAME, &iwr) >= 0 && !ffStrEqualsIgnCase(iwr.u.name, "IEEE 802.11")) + { + if(ffStrStartsWithIgnCase(iwr.u.name, "IEEE ")) + ffStrbufSetS(&item->conn.protocol, iwr.u.name + strlen("IEEE ")); + else + ffStrbufSetS(&item->conn.protocol, iwr.u.name); + } + + if(ioctl(sock, SIOCGIWAP, &iwr) >= 0) + { + for(int i = 0; i < 6; ++i) + ffStrbufAppendF(&item->conn.bssid, "%.2X:", (uint8_t) iwr.u.ap_addr.sa_data[i]); + ffStrbufTrimRight(&item->conn.bssid, '-'); + } + + if(ioctl(sock, SIOCGIWRATE, &iwr) >= 0) + item->conn.txRate = iwr.u.bitrate.value / 1000000.; + + struct iw_statistics stats; + iwr.u.data.pointer = &stats; + iwr.u.data.length = sizeof(stats); + iwr.u.data.flags = 0; + + if(ioctl(sock, SIOCGIWSTATS, &iwr) >= 0) + { + int8_t level = (int8_t) stats.qual.level; // https://stackoverflow.com/questions/18079771/wireless-h-how-do-i-print-out-the-signal-level + item->conn.signalQuality = level >= -50 ? 100 : level <= -100 ? 0 : (level + 100) * 2; + } + + //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) + { + 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; + } + } + + return NULL; +} +#endif // FF_HAVE_LINUX_WIRELESS + +const char* ffDetectWifi(FF_MAYBE_UNUSED FFlist* result) { struct if_nameindex* infs = if_nameindex(); if(!infs) return "if_nameindex() failed"; - FF_STRBUF_AUTO_DESTROY path = ffStrbufCreate(); + FF_STRBUF_AUTO_DESTROY buffer = ffStrbufCreate(); for(struct if_nameindex* i = infs; !(i->if_index == 0 && i->if_name == NULL); ++i) { - ffStrbufSetF(&path, "/sys/class/net/%s/phy80211", i->if_name); - if(!ffPathExists(path.chars, FF_PATHTYPE_DIRECTORY)) + ffStrbufSetF(&buffer, "/sys/class/net/%s/phy80211", i->if_name); + if(!ffPathExists(buffer.chars, FF_PATHTYPE_DIRECTORY)) continue; FFWifiResult* item = (FFWifiResult*)ffListAdd(result); @@ -242,109 +284,33 @@ static const char* detectWifiWithIoctls(FFlist* result) ffStrbufInit(&item->inf.status); ffStrbufInit(&item->conn.status); ffStrbufInit(&item->conn.ssid); - ffStrbufInit(&item->conn.macAddress); + ffStrbufInit(&item->conn.bssid); 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; - ffStrbufSetF(&path, "/sys/class/net/%s/operstate", i->if_name); - if (!ffAppendFileBuffer(path.chars, &item->inf.status)) + ffStrbufSetF(&buffer, "/sys/class/net/%s/operstate", i->if_name); + if (!ffAppendFileBuffer(buffer.chars, &item->inf.status)) continue; ffStrbufTrimRightSpace(&item->inf.status); if (!ffStrbufEqualS(&item->inf.status, "up")) continue; - FF_AUTO_CLOSE_FD int sock = socket(AF_INET, SOCK_DGRAM | SOCK_CLOEXEC, 0); - if(sock < 0) - continue; - - 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 && !ffStrEqualsIgnCase(iwr.u.name, "IEEE 802.11")) - { - if(ffStrStartsWithIgnCase(iwr.u.name, "IEEE ")) - ffStrbufSetS(&item->conn.protocol, iwr.u.name + strlen("IEEE ")); - 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, '-'); - } - - struct iw_statistics stats; - iwr.u.data.pointer = &stats; - iwr.u.data.length = sizeof(stats); - iwr.u.data.flags = 0; - - if(ioctl(sock, SIOCGIWSTATS, &iwr) >= 0) + if (detectWifiWithIw(item, &buffer) != NULL) { - int8_t level = (int8_t) stats.qual.level; // https://stackoverflow.com/questions/18079771/wireless-h-how-do-i-print-out-the-signal-level - item->conn.signalQuality = level >= -50 ? 100 : level <= -100 ? 0 : (level + 100) * 2; + #ifdef FF_HAVE_LINUX_WIRELESS + detectWifiWithIoctls(item); + #endif } - //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; - } - } + #ifdef FF_HAVE_DBUS + detectWifiWithNm(item, &buffer); + #endif } if_freenameindex(infs); return NULL; } - -#endif - -const char* ffDetectWifi(FF_MAYBE_UNUSED FFlist* result) -{ - #ifdef FF_HAVE_LIBNM - detectWifiWithLibnm(result); - if(result->length) // NetworkManager not enabled? #811 - return NULL; - #endif - - #ifdef FF_DETECT_WIFI_WITH_IOCTLS - return detectWifiWithIoctls(result); - #endif - - return "linux/wireless.h not found during compilation"; -} diff --git a/src/detection/wifi/wifi_windows.c b/src/detection/wifi/wifi_windows.c index 18638c046d..0508a72d44 100644 --- a/src/detection/wifi/wifi_windows.c +++ b/src/detection/wifi/wifi_windows.c @@ -76,7 +76,7 @@ const char* ffDetectWifi(FFlist* result) ffStrbufInit(&item->inf.status); ffStrbufInit(&item->conn.status); ffStrbufInit(&item->conn.ssid); - ffStrbufInit(&item->conn.macAddress); + ffStrbufInit(&item->conn.bssid); ffStrbufInit(&item->conn.protocol); ffStrbufInit(&item->conn.security); item->conn.signalQuality = 0.0/0.0; @@ -107,8 +107,8 @@ const char* ffDetectWifi(FFlist* result) (const char *)connInfo->wlanAssociationAttributes.dot11Ssid.ucSSID); for (size_t i = 0; i < sizeof(connInfo->wlanAssociationAttributes.dot11Bssid); i++) - ffStrbufAppendF(&item->conn.macAddress, "%.2X-", connInfo->wlanAssociationAttributes.dot11Bssid[i]); - ffStrbufTrimRight(&item->conn.macAddress, '-'); + ffStrbufAppendF(&item->conn.bssid, "%.2X-", connInfo->wlanAssociationAttributes.dot11Bssid[i]); + ffStrbufTrimRight(&item->conn.bssid, '-'); switch (connInfo->wlanAssociationAttributes.dot11PhyType) { diff --git a/src/logo/ascii/endeavour_small.txt b/src/logo/ascii/endeavour_small.txt index 7265f4dead..2bc9e93b0c 100644 --- a/src/logo/ascii/endeavour_small.txt +++ b/src/logo/ascii/endeavour_small.txt @@ -1,7 +1,7 @@ - /${c2}o${c3}. -${c1} :${c2}sssso${c3}- -${c1} :${c2}ossssssso${c3}: -${c1} /${c2}ssssssssssso${c3}+ -${c1} -+${c2}ssssssssssssssso${c3}+ -${c1} //${c2}osssssssssssssso${c3}+- - `+++++++++++++++-` + /${c2}o${c3}. +${c1} /${c2}sssso${c3}- +${c1} /${c2}ossssssso${c3}: +${c1} /${c2}ssssssssssso${c3}+ +${c1} /${c2}ssssssssssssssso${c3}+ +${c1}//${c2}osssssssssssssso${c3}+- + `+++++++++++++++-` diff --git a/src/logo/ascii/qts.txt b/src/logo/ascii/qts.txt new file mode 100644 index 0000000000..3fe32a8500 --- /dev/null +++ b/src/logo/ascii/qts.txt @@ -0,0 +1,14 @@ + $1###########################- + $1############################### +$1=#########**************######### +$1########## ######## +$1########## ######## +$1########## ######## +$1########## ######## +$1########## ######## +$1########## $2. $1######## +$1########## $2+=. $1:##### +$1+#########+=========- $2%%%* $1=# + $1#####################* $2%%%%# + $1-#####################* $2%%%%%. + $2``````` \ No newline at end of file diff --git a/src/logo/builtin.c b/src/logo/builtin.c index b23d3c3e93..49924eb081 100644 --- a/src/logo/builtin.c +++ b/src/logo/builtin.c @@ -3508,6 +3508,17 @@ static const FFlogo P[] = { }; static const FFlogo Q[] = { + // QTS + { + .names = {"qts"}, + .lines = FASTFETCH_DATATEXT_LOGO_QTS, + .colors = { + FF_COLOR_FG_BLUE, + FF_COLOR_FG_RED, + }, + .colorKeys = FF_COLOR_FG_BLUE, + .colorTitle = FF_COLOR_FG_BLUE, + }, // Q4OS { .names = {"Q4OS"}, diff --git a/src/modules/bluetooth/bluetooth.c b/src/modules/bluetooth/bluetooth.c index fa010ec9bb..037ca6335e 100644 --- a/src/modules/bluetooth/bluetooth.c +++ b/src/modules/bluetooth/bluetooth.c @@ -178,8 +178,8 @@ void ffGenerateBluetoothJsonResult(FF_MAYBE_UNUSED FFBluetoothOptions* options, void ffPrintBluetoothHelpFormat(void) { FF_PRINT_MODULE_FORMAT_HELP_CHECKED(FF_BLUETOOTH_MODULE_NAME, "{1} ({4})", FF_BLUETOOTH_NUM_FORMAT_ARGS, ((const char* []) { - "Name - name", - "Address - address", + "Name - device name", + "Address - remote device address", "Type - type", "Battery percentage number - battery-percentage", "Is connected - connected", @@ -192,7 +192,7 @@ void ffInitBluetoothOptions(FFBluetoothOptions* options) ffOptionInitModuleBaseInfo( &options->moduleInfo, FF_BLUETOOTH_MODULE_NAME, - "List bluetooth devices", + "List (connected) bluetooth devices", ffParseBluetoothCommandOptions, ffParseBluetoothJsonObject, ffPrintBluetooth, diff --git a/src/modules/bluetoothradio/bluetoothradio.c b/src/modules/bluetoothradio/bluetoothradio.c new file mode 100644 index 0000000000..455c06df2d --- /dev/null +++ b/src/modules/bluetoothradio/bluetoothradio.c @@ -0,0 +1,217 @@ +#include "common/percent.h" +#include "common/printing.h" +#include "common/jsonconfig.h" +#include "detection/bluetoothradio/bluetoothradio.h" +#include "modules/bluetoothradio/bluetoothradio.h" +#include "util/stringUtils.h" + +#define FF_BLUETOOTHRADIO_NUM_FORMAT_ARGS 8 +#define FF_BLUETOOTHRADIO_DISPLAY_NAME "Bluetooth Radio" + +static void printDevice(FFBluetoothRadioOptions* options, const FFBluetoothRadioResult* radio, uint8_t index) +{ + FF_STRBUF_AUTO_DESTROY key = ffStrbufCreate(); + if (options->moduleArgs.key.length == 0) + { + ffStrbufAppendF(&key, "%s (%s)", FF_BLUETOOTHRADIO_DISPLAY_NAME, radio->name.chars); + } + else + { + FF_PARSE_FORMAT_STRING_CHECKED(&key, &options->moduleArgs.key, 3, ((FFformatarg[]){ + {FF_FORMAT_ARG_TYPE_UINT, &index, "index"}, + {FF_FORMAT_ARG_TYPE_STRING, radio->name.chars, "name"}, + {FF_FORMAT_ARG_TYPE_STRBUF, &options->moduleArgs.keyIcon, "icon"}, + })); + } + + const char* version = NULL; + + switch (radio->lmpVersion < 0 ? -radio->lmpVersion : radio->lmpVersion) + { + case 0: version = "1.0b"; break; + case 1: version = "1.1"; break; + case 2: version = "1.2"; break; + case 3: version = "2.0"; break; + case 4: version = "2.1"; break; + case 5: version = "3.0"; break; + case 6: version = "4.0"; break; + case 7: version = "4.1"; break; + case 8: version = "4.2"; break; + case 9: version = "5.0"; break; + case 10: version = "5.1"; break; + case 11: version = "5.2"; break; + case 12: version = "5.3"; break; + case 13: version = "5.4"; break; + } + + if(options->moduleArgs.outputFormat.length == 0) + { + ffPrintLogoAndKey(key.chars, 0, &options->moduleArgs, FF_PRINT_TYPE_NO_CUSTOM_KEY); + + if (version) + printf("Bluetooth %s%s (%s)\n", version, (radio->lmpVersion < 0 ? "+" : ""), radio->vendor.chars); + else + ffStrbufPutTo(&radio->vendor, stdout); + } + else + { + FF_PRINT_FORMAT_CHECKED(key.chars, 0, &options->moduleArgs, FF_PRINT_TYPE_NO_CUSTOM_KEY, FF_BLUETOOTHRADIO_NUM_FORMAT_ARGS, ((FFformatarg[]) { + {FF_FORMAT_ARG_TYPE_STRBUF, &radio->name, "name"}, + {FF_FORMAT_ARG_TYPE_STRBUF, &radio->address, "address"}, + {FF_FORMAT_ARG_TYPE_INT, &radio->lmpVersion, "lmp-version"}, + {FF_FORMAT_ARG_TYPE_INT, &radio->lmpSubversion, "lmp-subversion"}, + {FF_FORMAT_ARG_TYPE_STRING, version, "version"}, + {FF_FORMAT_ARG_TYPE_STRBUF, &radio->vendor, "vendor"}, + {FF_FORMAT_ARG_TYPE_BOOL, &radio->discoverable, "discoverable"}, + {FF_FORMAT_ARG_TYPE_BOOL, &radio->connectable, "connectable"}, + })); + } +} + +void ffPrintBluetoothRadio(FFBluetoothRadioOptions* options) +{ + FF_LIST_AUTO_DESTROY radios = ffListCreate(sizeof (FFBluetoothRadioResult)); + const char* error = ffDetectBluetoothRadio(&radios); + + if(error) + { + ffPrintError(FF_BLUETOOTHRADIO_DISPLAY_NAME, 0, &options->moduleArgs, FF_PRINT_TYPE_DEFAULT, "%s", error); + } + else + { + uint8_t index = 0; + FF_LIST_FOR_EACH(FFBluetoothRadioResult, radio, radios) + { + if (!radio->enabled) + continue; + + index++; + printDevice(options, radio, index); + } + + if (index == 0) + { + if (radios.length > 0) + ffPrintError(FF_BLUETOOTHRADIO_DISPLAY_NAME, 0, &options->moduleArgs, FF_PRINT_TYPE_DEFAULT, "Bluetooth radios found but none enabled"); + else + ffPrintError(FF_BLUETOOTHRADIO_DISPLAY_NAME, 0, &options->moduleArgs, FF_PRINT_TYPE_DEFAULT, "No devices detected"); + } + } + + FF_LIST_FOR_EACH(FFBluetoothRadioResult, radio, radios) + { + ffStrbufDestroy(&radio->name); + ffStrbufDestroy(&radio->address); + ffStrbufDestroy(&radio->vendor); + } +} + +bool ffParseBluetoothRadioCommandOptions(FFBluetoothRadioOptions* options, const char* key, const char* value) +{ + const char* subKey = ffOptionTestPrefix(key, FF_BLUETOOTHRADIO_MODULE_NAME); + if (!subKey) return false; + if (ffOptionParseModuleArgs(key, subKey, value, &options->moduleArgs)) + return true; + + return false; +} + +void ffParseBluetoothRadioJsonObject(FFBluetoothRadioOptions* options, yyjson_val* module) +{ + yyjson_val *key_, *val; + size_t idx, max; + yyjson_obj_foreach(module, idx, max, key_, val) + { + const char* key = yyjson_get_str(key_); + if(ffStrEqualsIgnCase(key, "type")) + continue; + + if (ffJsonConfigParseModuleArgs(key, val, &options->moduleArgs)) + continue; + + ffPrintError(FF_BLUETOOTHRADIO_MODULE_NAME, 0, &options->moduleArgs, FF_PRINT_TYPE_DEFAULT, "Unknown JSON key %s", key); + } +} + +void ffGenerateBluetoothRadioJsonConfig(FFBluetoothRadioOptions* options, yyjson_mut_doc* doc, yyjson_mut_val* module) +{ + __attribute__((__cleanup__(ffDestroyBluetoothRadioOptions))) FFBluetoothRadioOptions defaultOptions; + ffInitBluetoothRadioOptions(&defaultOptions); + + ffJsonConfigGenerateModuleArgsConfig(doc, module, &defaultOptions.moduleArgs, &options->moduleArgs); +} + +void ffGenerateBluetoothRadioJsonResult(FF_MAYBE_UNUSED FFBluetoothRadioOptions* options, yyjson_mut_doc* doc, yyjson_mut_val* module) +{ + FF_LIST_AUTO_DESTROY results = ffListCreate(sizeof(FFBluetoothRadioResult)); + + const char* error = ffDetectBluetoothRadio(&results); + if (error) + { + yyjson_mut_obj_add_str(doc, module, "error", error); + return; + } + + yyjson_mut_val* arr = yyjson_mut_obj_add_arr(doc, module, "result"); + + FF_LIST_FOR_EACH(FFBluetoothRadioResult, item, results) + { + yyjson_mut_val* obj = yyjson_mut_arr_add_obj(doc, arr); + yyjson_mut_obj_add_strbuf(doc, obj, "name", &item->name); + yyjson_mut_obj_add_strbuf(doc, obj, "address", &item->address); + if (item->lmpVersion == INT_MIN) + yyjson_mut_obj_add_null(doc, obj, "lmpVersion"); + else + yyjson_mut_obj_add_int(doc, obj, "lmpVersion", item->lmpVersion); + if (item->lmpSubversion == INT_MIN) + yyjson_mut_obj_add_null(doc, obj, "lmpSubversion"); + else + yyjson_mut_obj_add_int(doc, obj, "lmpSubversion", item->lmpSubversion); + yyjson_mut_obj_add_strbuf(doc, obj, "vendor", &item->vendor); + yyjson_mut_obj_add_bool(doc, obj, "enabled", item->enabled); + yyjson_mut_obj_add_bool(doc, obj, "discoverable", item->discoverable); + yyjson_mut_obj_add_bool(doc, obj, "connectable", item->connectable); + } + + FF_LIST_FOR_EACH(FFBluetoothRadioResult, radio, results) + { + ffStrbufDestroy(&radio->name); + ffStrbufDestroy(&radio->address); + ffStrbufDestroy(&radio->vendor); + } +} + +void ffPrintBluetoothRadioHelpFormat(void) +{ + FF_PRINT_MODULE_FORMAT_HELP_CHECKED(FF_BLUETOOTHRADIO_MODULE_NAME, "Bluetooth {5} ({6})", FF_BLUETOOTHRADIO_NUM_FORMAT_ARGS, ((const char* []) { + "Radio name for discovering - name", + "Address - local radio address", + "LMP version - lmp-version", + "LMP subversion - lmp-subversion", + "Bluetooth version - version", + "Vendor - vendor", + "Discoverable - discoverable", + "Connectable / Pairable - connectable", + })); +} + +void ffInitBluetoothRadioOptions(FFBluetoothRadioOptions* options) +{ + ffOptionInitModuleBaseInfo( + &options->moduleInfo, + FF_BLUETOOTHRADIO_MODULE_NAME, + "List bluetooth radios width supported version and vendor", + ffParseBluetoothRadioCommandOptions, + ffParseBluetoothRadioJsonObject, + ffPrintBluetoothRadio, + ffGenerateBluetoothRadioJsonResult, + ffPrintBluetoothRadioHelpFormat, + ffGenerateBluetoothRadioJsonConfig + ); + ffOptionInitModuleArg(&options->moduleArgs, "󰐻"); +} + +void ffDestroyBluetoothRadioOptions(FFBluetoothRadioOptions* options) +{ + ffOptionDestroyModuleArg(&options->moduleArgs); +} diff --git a/src/modules/bluetoothradio/bluetoothradio.h b/src/modules/bluetoothradio/bluetoothradio.h new file mode 100644 index 0000000000..ee9ef11959 --- /dev/null +++ b/src/modules/bluetoothradio/bluetoothradio.h @@ -0,0 +1,9 @@ +#pragma once + +#include "fastfetch.h" + +#define FF_BLUETOOTHRADIO_MODULE_NAME "BluetoothRadio" + +void ffPrintBluetoothRadio(FFBluetoothRadioOptions* options); +void ffInitBluetoothRadioOptions(FFBluetoothRadioOptions* options); +void ffDestroyBluetoothRadioOptions(FFBluetoothRadioOptions* options); diff --git a/src/modules/bluetoothradio/option.h b/src/modules/bluetoothradio/option.h new file mode 100644 index 0000000000..714200d3e8 --- /dev/null +++ b/src/modules/bluetoothradio/option.h @@ -0,0 +1,12 @@ +#pragma once + +// This file will be included in "fastfetch.h", do NOT put unnecessary things here + +#include "common/option.h" +#include "common/percent.h" + +typedef struct FFBluetoothRadioOptions +{ + FFModuleBaseInfo moduleInfo; + FFModuleArgs moduleArgs; +} FFBluetoothRadioOptions; diff --git a/src/modules/cpu/cpu.c b/src/modules/cpu/cpu.c index 72391793b4..65503eb9a7 100644 --- a/src/modules/cpu/cpu.c +++ b/src/modules/cpu/cpu.c @@ -95,7 +95,7 @@ void ffPrintCPU(FFCPUOptions* options) FF_STRBUF_AUTO_DESTROY freqBase = ffStrbufCreate(); ffParseFrequency(cpu.frequencyBase, &freqBase); FF_STRBUF_AUTO_DESTROY freqMax = ffStrbufCreate(); - ffParseFrequency(cpu.frequencyBase, &freqMax); + ffParseFrequency(cpu.frequencyMax, &freqMax); FF_STRBUF_AUTO_DESTROY freqBioslimit = ffStrbufCreate(); ffParseFrequency(cpu.frequencyBiosLimit, &freqBioslimit); diff --git a/src/modules/gpu/gpu.c b/src/modules/gpu/gpu.c index 6c89d806a1..0765c8af1c 100644 --- a/src/modules/gpu/gpu.c +++ b/src/modules/gpu/gpu.c @@ -327,6 +327,8 @@ void ffGenerateGPUJsonResult(FFGPUOptions* options, yyjson_mut_doc* doc, yyjson_ else yyjson_mut_obj_add_null(doc, obj, "coreCount"); + yyjson_mut_obj_add_real(doc, obj, "coreUsage", gpu->coreUsage); + yyjson_mut_val* memoryObj = yyjson_mut_obj_add_obj(doc, obj, "memory"); yyjson_mut_val* dedicatedObj = yyjson_mut_obj_add_obj(doc, memoryObj, "dedicated"); @@ -371,7 +373,7 @@ void ffGenerateGPUJsonResult(FFGPUOptions* options, yyjson_mut_doc* doc, yyjson_ yyjson_mut_obj_add_strbuf(doc, obj, "platformApi", &gpu->platformApi); - yyjson_mut_obj_add_real(doc, obj, "frequency", gpu->frequency); // NaN will be output as "null" + yyjson_mut_obj_add_uint(doc, obj, "frequency", gpu->frequency); yyjson_mut_obj_add_uint(doc, obj, "deviceId", gpu->deviceId); } diff --git a/src/modules/modules.h b/src/modules/modules.h index 58ce1b98d4..dbfebcf319 100644 --- a/src/modules/modules.h +++ b/src/modules/modules.h @@ -5,6 +5,7 @@ #include "modules/battery/battery.h" #include "modules/bios/bios.h" #include "modules/bluetooth/bluetooth.h" +#include "modules/bluetoothradio/bluetoothradio.h" #include "modules/brightness/brightness.h" #include "modules/board/board.h" #include "modules/bootmgr/bootmgr.h" diff --git a/src/modules/monitor/monitor.c b/src/modules/monitor/monitor.c index d83e3431e3..6fdd53eb5c 100644 --- a/src/modules/monitor/monitor.c +++ b/src/modules/monitor/monitor.c @@ -54,7 +54,7 @@ void ffPrintMonitor(FFMonitorOptions* options) printf("%ux%u px", display->width, display->height); if (display->refreshRate > 0) - printf(" @ %.3f Hz", display->refreshRate); + printf(" @ %g Hz", ((int) (display->refreshRate * 1000 + 0.5)) / 1000.0); if (inch > 0) printf(" - %ux%u mm (%.2f inches, %.2f ppi)\n", display->physicalWidth, display->physicalHeight, inch, ppi); else diff --git a/src/modules/opencl/opencl.c b/src/modules/opencl/opencl.c index 2f69be3bf0..cb29a155e0 100644 --- a/src/modules/opencl/opencl.c +++ b/src/modules/opencl/opencl.c @@ -96,7 +96,7 @@ void ffGenerateOpenCLJsonResult(FF_MAYBE_UNUSED FFOpenCLOptions* options, yyjson else yyjson_mut_obj_add_null(doc, gpuObj, "coreCount"); - yyjson_mut_obj_add_real(doc, gpuObj, "frequency", gpu->frequency); + yyjson_mut_obj_add_uint(doc, gpuObj, "frequency", gpu->frequency); yyjson_mut_val* memoryObj = yyjson_mut_obj_add_obj(doc, gpuObj, "memory"); diff --git a/src/modules/options.h b/src/modules/options.h index 49bd8fe6b5..c705c75005 100644 --- a/src/modules/options.h +++ b/src/modules/options.h @@ -5,6 +5,7 @@ #include "modules/battery/option.h" #include "modules/bios/option.h" #include "modules/bluetooth/option.h" +#include "modules/bluetoothradio/option.h" #include "modules/board/option.h" #include "modules/bootmgr/option.h" #include "modules/break/option.h" diff --git a/src/modules/physicalmemory/physicalmemory.c b/src/modules/physicalmemory/physicalmemory.c index 84e62eb904..6949b045d8 100644 --- a/src/modules/physicalmemory/physicalmemory.c +++ b/src/modules/physicalmemory/physicalmemory.c @@ -20,6 +20,12 @@ void ffPrintPhysicalMemory(FFPhysicalMemoryOptions* options) return; } + if (result.length == 0) + { + ffPrintError(FF_PHYSICALMEMORY_DISPLAY_NAME, 0, &options->moduleArgs, FF_PRINT_TYPE_DEFAULT, "No physical memory detected"); + return; + } + FF_STRBUF_AUTO_DESTROY prettySize = ffStrbufCreate(); uint32_t i = 0; diff --git a/src/modules/wifi/wifi.c b/src/modules/wifi/wifi.c index 18208d3a84..fa2ddb05d9 100644 --- a/src/modules/wifi/wifi.c +++ b/src/modules/wifi/wifi.c @@ -85,7 +85,7 @@ void ffPrintWifi(FFWifiOptions* options) {FF_FORMAT_ARG_TYPE_STRBUF, &item->inf.status, "inf-status"}, {FF_FORMAT_ARG_TYPE_STRBUF, &item->conn.status, "status"}, {FF_FORMAT_ARG_TYPE_STRBUF, &item->conn.ssid, "ssid"}, - {FF_FORMAT_ARG_TYPE_STRBUF, &item->conn.macAddress, "mac-address"}, + {FF_FORMAT_ARG_TYPE_STRBUF, &item->conn.bssid, "bssid"}, {FF_FORMAT_ARG_TYPE_STRBUF, &item->conn.protocol, "protocol"}, {FF_FORMAT_ARG_TYPE_DOUBLE, &percentNum, "signal-quality"}, {FF_FORMAT_ARG_TYPE_DOUBLE, &item->conn.rxRate, "rx-rate"}, @@ -99,7 +99,7 @@ void ffPrintWifi(FFWifiOptions* options) ffStrbufDestroy(&item->inf.status); ffStrbufDestroy(&item->conn.status); ffStrbufDestroy(&item->conn.ssid); - ffStrbufDestroy(&item->conn.macAddress); + ffStrbufDestroy(&item->conn.bssid); ffStrbufDestroy(&item->conn.protocol); ffStrbufDestroy(&item->conn.security); } @@ -170,7 +170,7 @@ void ffGenerateWifiJsonResult(FF_MAYBE_UNUSED FFWifiOptions* options, yyjson_mut yyjson_mut_val* conn = yyjson_mut_obj_add_obj(doc, obj, "conn"); yyjson_mut_obj_add_strbuf(doc, conn, "status", &wifi->conn.status); yyjson_mut_obj_add_strbuf(doc, conn, "ssid", &wifi->conn.ssid); - yyjson_mut_obj_add_strbuf(doc, conn, "bssid", &wifi->conn.macAddress); + yyjson_mut_obj_add_strbuf(doc, conn, "bssid", &wifi->conn.bssid); yyjson_mut_obj_add_strbuf(doc, conn, "protocol", &wifi->conn.protocol); yyjson_mut_obj_add_strbuf(doc, conn, "security", &wifi->conn.security); yyjson_mut_obj_add_real(doc, conn, "signalQuality", wifi->conn.signalQuality); @@ -184,7 +184,7 @@ void ffGenerateWifiJsonResult(FF_MAYBE_UNUSED FFWifiOptions* options, yyjson_mut ffStrbufDestroy(&item->inf.status); ffStrbufDestroy(&item->conn.status); ffStrbufDestroy(&item->conn.ssid); - ffStrbufDestroy(&item->conn.macAddress); + ffStrbufDestroy(&item->conn.bssid); ffStrbufDestroy(&item->conn.protocol); ffStrbufDestroy(&item->conn.security); } diff --git a/src/options/library.c b/src/options/library.c index a80f4b6c60..c718f6bbc7 100644 --- a/src/options/library.c +++ b/src/options/library.c @@ -62,8 +62,6 @@ const char* ffOptionsParseLibraryJsonConfig(FFOptionsLibrary* options, yyjson_va ffStrbufSetS(&options->libOSMesa, yyjson_get_str(val)); else if (ffStrEqualsIgnCase(key, "pulse")) ffStrbufSetS(&options->libPulse, yyjson_get_str(val)); - else if (ffStrEqualsIgnCase(key, "nm")) - ffStrbufSetS(&options->libnm, yyjson_get_str(val)); else if (ffStrEqualsIgnCase(key, "ddcutil")) ffStrbufSetS(&options->libDdcutil, yyjson_get_str(val)); else if (ffStrEqualsIgnCase(key, "drm")) @@ -129,8 +127,6 @@ bool ffOptionsParseLibraryCommandLine(FFOptionsLibrary* options, const char* key ffOptionParseString(key, value, &options->libOSMesa); else if(ffStrEqualsIgnCase(subkey, "pulse")) ffOptionParseString(key, value, &options->libPulse); - else if(ffStrEqualsIgnCase(subkey, "nm")) - ffOptionParseString(key, value, &options->libnm); else if(ffStrEqualsIgnCase(subkey, "ddcutil")) ffOptionParseString(key, value, &options->libDdcutil); else if(ffStrEqualsIgnCase(subkey, "drm")) @@ -227,9 +223,6 @@ void ffOptionsGenerateLibraryJsonConfig(FFOptionsLibrary* options, yyjson_mut_do if (!ffStrbufEqual(&options->libPulse, &defaultOptions.libPulse)) yyjson_mut_obj_add_strbuf(doc, obj, "pulse", &options->libPulse); - if (!ffStrbufEqual(&options->libnm, &defaultOptions.libnm)) - yyjson_mut_obj_add_strbuf(doc, obj, "nm", &options->libnm); - if (!ffStrbufEqual(&options->libDdcutil, &defaultOptions.libDdcutil)) yyjson_mut_obj_add_strbuf(doc, obj, "ddcutil", &options->libDdcutil); diff --git a/src/options/library.h b/src/options/library.h index 55f2be4e22..14d38a7289 100644 --- a/src/options/library.h +++ b/src/options/library.h @@ -30,7 +30,6 @@ typedef struct FFOptionsLibrary FFstrbuf libGLX; FFstrbuf libOSMesa; FFstrbuf libPulse; - FFstrbuf libnm; FFstrbuf libDdcutil; FFstrbuf libdrm; #endif diff --git a/src/options/modules.c b/src/options/modules.c index 7d80b2d868..0bc117e3eb 100644 --- a/src/options/modules.c +++ b/src/options/modules.c @@ -6,6 +6,7 @@ void ffOptionsInitModules(FFOptionsModules* options) ffInitBatteryOptions(&options->battery); ffInitBiosOptions(&options->bios); ffInitBluetoothOptions(&options->bluetooth); + ffInitBluetoothRadioOptions(&options->bluetoothRadio); ffInitBoardOptions(&options->board); ffInitBootmgrOptions(&options->bootmgr); ffInitBreakOptions(&options->break_); @@ -77,6 +78,7 @@ void ffOptionsDestroyModules(FFOptionsModules* options) ffDestroyBatteryOptions(&options->battery); ffDestroyBiosOptions(&options->bios); ffDestroyBluetoothOptions(&options->bluetooth); + ffDestroyBluetoothRadioOptions(&options->bluetoothRadio); ffDestroyBoardOptions(&options->board); ffDestroyBootmgrOptions(&options->bootmgr); ffDestroyBreakOptions(&options->break_); diff --git a/src/options/modules.h b/src/options/modules.h index fdfae5e47a..1768c8c181 100644 --- a/src/options/modules.h +++ b/src/options/modules.h @@ -7,6 +7,7 @@ typedef struct FFOptionsModules FFBatteryOptions battery; FFBiosOptions bios; FFBluetoothOptions bluetooth; + FFBluetoothRadioOptions bluetoothRadio; FFBoardOptions board; FFBootmgrOptions bootmgr; FFBreakOptions break_;