mirror of
https://github.com/fastfetch-cli/fastfetch.git
synced 2026-09-13 02:42:09 +02:00
+19
-30
@@ -38,7 +38,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_DRM_AMDGPU=OFF -DENABLE_GIO=OFF -DENABLE_DCONF=OFF -DENABLE_DBUS=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_OPENCL=OFF -DENABLE_FREETYPE=OFF -DENABLE_PULSE=OFF -DENABLE_DDCUTIL=OFF -DENABLE_ELF=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_DRM_AMDGPU=OFF -DENABLE_GIO=OFF -DENABLE_DCONF=OFF -DENABLE_DBUS=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_OPENCL=OFF -DENABLE_FREETYPE=OFF -DENABLE_PULSE=OFF -DENABLE_DDCUTIL=OFF -DENABLE_ELF=OFF -DENABLE_THREADS=OFF
|
||||
|
||||
- name: build project
|
||||
run: cmake --build . --target package --verbose -j4
|
||||
@@ -93,7 +93,7 @@ jobs:
|
||||
run: sudo add-apt-repository -y ppa:ubuntu-toolchain-r/test
|
||||
|
||||
- name: install required packages
|
||||
run: sudo apt-get update && sudo apt-get install -y gcc-13 g++-13 libvulkan-dev libwayland-dev libxrandr-dev libxcb-randr0-dev libdconf-dev libdbus-1-dev libmagickcore-dev libsqlite3-dev librpm-dev libegl-dev libglx-dev ocl-icd-opencl-dev libpulse-dev libdrm-dev libelf-dev libddcutil-dev directx-headers-dev rpm ninja-build
|
||||
run: sudo apt-get update && sudo apt-get install -y gcc-13 libvulkan-dev libwayland-dev libxrandr-dev libxcb-randr0-dev libdconf-dev libdbus-1-dev libmagickcore-dev libsqlite3-dev librpm-dev libegl-dev libglx-dev ocl-icd-opencl-dev libpulse-dev libdrm-dev libelf-dev libddcutil-dev rpm ninja-build
|
||||
|
||||
- name: install linuxbrew packages
|
||||
run: |
|
||||
@@ -107,7 +107,7 @@ jobs:
|
||||
languages: c
|
||||
|
||||
- name: configure project
|
||||
run: CC=gcc-13 CXX=g++-13 PKG_CONFIG_PATH=/home/linuxbrew/.linuxbrew/lib/pkgconfig:$PKG_CONFIG_PATH cmake -GNinja -DSET_TWEAK=Off -DBUILD_TESTS=On -DENABLE_EMBEDDED_PCIIDS=On -DENABLE_EMBEDDED_AMDGPUIDS=On -DCMAKE_INSTALL_PREFIX=/usr .
|
||||
run: CC=gcc-13 PKG_CONFIG_PATH=/home/linuxbrew/.linuxbrew/lib/pkgconfig:$PKG_CONFIG_PATH cmake -GNinja -DSET_TWEAK=Off -DBUILD_TESTS=On -DENABLE_EMBEDDED_PCIIDS=On -DENABLE_EMBEDDED_AMDGPUIDS=On -DCMAKE_INSTALL_PREFIX=/usr .
|
||||
|
||||
- name: build project
|
||||
run: cmake --build . --target package --verbose -j4
|
||||
@@ -190,7 +190,7 @@ jobs:
|
||||
run: cmake --version
|
||||
|
||||
- name: configure project
|
||||
run: CC=gcc-13 PKG_CONFIG_PATH=/home/linuxbrew/.linuxbrew/lib/pkgconfig:$PKG_CONFIG_PATH cmake -DCMAKE_C_FLAGS="-m32 -march=i686 -mtune=i686" -DCMAKE_SYSTEM_PROCESSOR_OVERRIDE=i686 -DCPACK_DEBIAN_PACKAGE_ARCHITECTURE=i386 -GNinja -DSET_TWEAK=Off -DBUILD_TESTS=On -DENABLE_EMBEDDED_PCIIDS=On -DENABLE_EMBEDDED_AMDGPUIDS=On -DCMAKE_INSTALL_PREFIX=/usr -DENABLE_DIRECTX_HEADERS=Off .
|
||||
run: CC=gcc-13 PKG_CONFIG_PATH=/home/linuxbrew/.linuxbrew/lib/pkgconfig:$PKG_CONFIG_PATH cmake -DCMAKE_C_FLAGS="-m32 -march=i686 -mtune=i686" -DCMAKE_SYSTEM_PROCESSOR_OVERRIDE=i686 -DCPACK_DEBIAN_PACKAGE_ARCHITECTURE=i386 -GNinja -DSET_TWEAK=Off -DBUILD_TESTS=On -DENABLE_EMBEDDED_PCIIDS=On -DENABLE_EMBEDDED_AMDGPUIDS=On -DCMAKE_INSTALL_PREFIX=/usr .
|
||||
|
||||
- name: build project
|
||||
run: cmake --build . --target package --verbose -j4
|
||||
@@ -237,17 +237,17 @@ jobs:
|
||||
id: runcmd
|
||||
with:
|
||||
arch: armv7
|
||||
distro: ubuntu20.04
|
||||
distro: ubuntu22.04
|
||||
githubToken: ${{ github.token }}
|
||||
run: |
|
||||
uname -a
|
||||
apt-get update && apt-get install -y software-properties-common ca-certificates gpg curl
|
||||
add-apt-repository -y ppa:ubuntu-toolchain-r/test
|
||||
curl -L https://apt.kitware.com/keys/kitware-archive-latest.asc | gpg --dearmor - | tee /usr/share/keyrings/kitware-archive-keyring.gpg >/dev/null
|
||||
echo 'deb [signed-by=/usr/share/keyrings/kitware-archive-keyring.gpg] https://apt.kitware.com/ubuntu/ focal main' | tee /etc/apt/sources.list.d/kitware.list >/dev/null
|
||||
echo 'deb [signed-by=/usr/share/keyrings/kitware-archive-keyring.gpg] https://apt.kitware.com/ubuntu/ jammy main' | tee /etc/apt/sources.list.d/kitware.list >/dev/null
|
||||
echo -e 'Acquire::https::Verify-Peer "false";\nAcquire::https::Verify-Host "false";' >> /etc/apt/apt.conf.d/99ignore-certificates
|
||||
apt-get update && apt-get install -y cmake make gcc-13 libvulkan-dev libwayland-dev libxrandr-dev libxcb-randr0-dev libdconf-dev libdbus-1-dev libmagickcore-dev libsqlite3-dev librpm-dev libegl-dev libglx-dev ocl-icd-opencl-dev libpulse-dev libdrm-dev libelf-dev rpm
|
||||
CC=gcc-13 cmake -DSET_TWEAK=Off -DBUILD_TESTS=On -DENABLE_DIRECTX_HEADERS=Off -DCMAKE_INSTALL_PREFIX=/usr .
|
||||
CC=gcc-13 cmake -DSET_TWEAK=Off -DBUILD_TESTS=On -DCMAKE_INSTALL_PREFIX=/usr .
|
||||
cmake --build . --target package --verbose -j4
|
||||
./fastfetch --list-features
|
||||
time ./fastfetch -c presets/ci.jsonc --stat false
|
||||
@@ -283,7 +283,7 @@ jobs:
|
||||
uname -a
|
||||
apt-get update && apt-get install -y wget
|
||||
apt-get install -y cmake make gcc libvulkan-dev libwayland-dev libxrandr-dev libxcb-randr0-dev libdconf-dev libdbus-1-dev libmagickcore-dev libsqlite3-dev librpm-dev libegl-dev libglx-dev ocl-icd-opencl-dev libpulse-dev libdrm-dev libelf-dev rpm
|
||||
cmake -DSET_TWEAK=Off -DBUILD_TESTS=On -DENABLE_DIRECTX_HEADERS=Off -DCMAKE_INSTALL_PREFIX=/usr .
|
||||
cmake -DSET_TWEAK=Off -DBUILD_TESTS=On -DCMAKE_INSTALL_PREFIX=/usr .
|
||||
cmake --build . --target package --verbose -j4
|
||||
./fastfetch --list-features
|
||||
time ./fastfetch -c presets/ci.jsonc --stat false
|
||||
@@ -326,7 +326,7 @@ jobs:
|
||||
apt-get update && apt-get install -y software-properties-common
|
||||
add-apt-repository -y ppa:ubuntu-toolchain-r/test
|
||||
apt-get update && apt-get install -y cmake make gcc-13 libvulkan-dev libwayland-dev libxrandr-dev libxcb-randr0-dev libdconf-dev libdbus-1-dev libmagickcore-dev libsqlite3-dev librpm-dev libegl-dev libglx-dev ocl-icd-opencl-dev libpulse-dev libdrm-dev libchafa-dev libelf-dev rpm
|
||||
CC=gcc-13 cmake -DSET_TWEAK=Off -DBUILD_TESTS=On -DENABLE_DIRECTX_HEADERS=Off -DCMAKE_INSTALL_PREFIX=/usr .
|
||||
CC=gcc-13 cmake -DSET_TWEAK=Off -DBUILD_TESTS=On -DCMAKE_INSTALL_PREFIX=/usr .
|
||||
cmake --build . --target package --verbose -j4
|
||||
./fastfetch --list-features
|
||||
time ./fastfetch -c presets/ci.jsonc --stat false
|
||||
@@ -413,10 +413,10 @@ jobs:
|
||||
run: ./fastfetch --list-features
|
||||
|
||||
- name: run fastfetch
|
||||
run: time ./fastfetch -c presets/ci.jsonc --stat false
|
||||
run: time ./fastfetch -c presets/ci.jsonc --structure-disabled vulkan --stat false
|
||||
|
||||
- name: run fastfetch --format json
|
||||
run: time ./fastfetch -c presets/ci.jsonc --format json
|
||||
run: time ./fastfetch -c presets/ci.jsonc --structure-disabled vulkan --format json
|
||||
|
||||
- name: run flashfetch
|
||||
run: time ./flashfetch
|
||||
@@ -690,7 +690,7 @@ jobs:
|
||||
path: ./fastfetch-*.*
|
||||
|
||||
windows-hosts:
|
||||
name: Windows-${{ matrix.arch }}${{ matrix.win7-compat-postfix }}
|
||||
name: Windows-${{ matrix.arch }}
|
||||
runs-on: ${{ matrix.runs-on }}
|
||||
permissions:
|
||||
security-events: write
|
||||
@@ -703,22 +703,11 @@ jobs:
|
||||
msystem: CLANG64
|
||||
msystem-lower: clang64
|
||||
msys-arch: x86_64
|
||||
win7-compat-flag: OFF
|
||||
win7-compat-postfix: ""
|
||||
- arch: amd64
|
||||
runs-on: windows-latest
|
||||
msystem: CLANG64
|
||||
msystem-lower: clang64
|
||||
msys-arch: x86_64
|
||||
win7-compat-flag: ON
|
||||
win7-compat-postfix: "-win7"
|
||||
- arch: aarch64
|
||||
runs-on: windows-11-arm
|
||||
msystem: CLANGARM64
|
||||
msystem-lower: clangarm64
|
||||
msys-arch: aarch64
|
||||
win7-compat-flag: OFF
|
||||
win7-compat-postfix: ""
|
||||
defaults:
|
||||
run:
|
||||
shell: msys2 {0}
|
||||
@@ -731,13 +720,13 @@ jobs:
|
||||
with:
|
||||
msystem: ${{ matrix.msystem }}
|
||||
update: true
|
||||
install: git mingw-w64-clang-${{ matrix.msys-arch }}-7zip mingw-w64-clang-${{ matrix.msys-arch }}-cmake mingw-w64-clang-${{ matrix.msys-arch }}-clang mingw-w64-clang-${{ matrix.msys-arch }}-vulkan-loader mingw-w64-clang-${{ matrix.msys-arch }}-vulkan-headers mingw-w64-clang-${{ matrix.msys-arch }}-opencl-icd mingw-w64-clang-${{ matrix.msys-arch }}-opencl-headers mingw-w64-clang-${{ matrix.msys-arch }}-cppwinrt mingw-w64-clang-${{ matrix.msys-arch }}-imagemagick mingw-w64-clang-${{ matrix.msys-arch }}-chafa
|
||||
install: git mingw-w64-clang-${{ matrix.msys-arch }}-7zip mingw-w64-clang-${{ matrix.msys-arch }}-cmake mingw-w64-clang-${{ matrix.msys-arch }}-clang mingw-w64-clang-${{ matrix.msys-arch }}-vulkan-loader mingw-w64-clang-${{ matrix.msys-arch }}-vulkan-headers mingw-w64-clang-${{ matrix.msys-arch }}-opencl-icd mingw-w64-clang-${{ matrix.msys-arch }}-opencl-headers mingw-w64-clang-${{ matrix.msys-arch }}-cppwinrt mingw-w64-clang-${{ matrix.msys-arch }}-imagemagick mingw-w64-clang-${{ matrix.msys-arch }}-chafa mingw-w64-clang-${{ matrix.msys-arch }}-directx-headers
|
||||
|
||||
- name: print msys version
|
||||
run: uname -a
|
||||
|
||||
- name: configure project
|
||||
run: env PKG_CONFIG_PATH=/${{ matrix.msystem-lower }}/lib/pkgconfig/:$PKG_CONFIG_PATH cmake -DSET_TWEAK=Off -DBUILD_TESTS=On -DENABLE_WIN7_COMPAT=${{ matrix.win7-compat-flag }} .
|
||||
run: env PKG_CONFIG_PATH=/${{ matrix.msystem-lower }}/lib/pkgconfig/:$PKG_CONFIG_PATH cmake -DSET_TWEAK=Off -DBUILD_TESTS=On .
|
||||
|
||||
- name: build project
|
||||
run: cmake --build . --verbose -j4
|
||||
@@ -768,7 +757,7 @@ jobs:
|
||||
name: upload artifacts for signing
|
||||
uses: actions/upload-artifact@v7
|
||||
with:
|
||||
name: fastfetch-windows-${{ matrix.arch }}${{ matrix.win7-compat-postfix }}
|
||||
name: fastfetch-windows-${{ matrix.arch }}
|
||||
path: |
|
||||
*.dll
|
||||
fastfetch.exe
|
||||
@@ -787,16 +776,16 @@ jobs:
|
||||
output-artifact-directory: '.'
|
||||
|
||||
- name: create zip archive
|
||||
run: 7z a -tzip -mx9 -bd -y fastfetch-windows-${{ matrix.arch }}${{ matrix.win7-compat-postfix }}.zip LICENSE *.dll fastfetch.exe flashfetch.exe presets
|
||||
run: 7z a -tzip -mx9 -bd -y fastfetch-windows-${{ matrix.arch }}.zip LICENSE *.dll fastfetch.exe flashfetch.exe presets
|
||||
|
||||
- name: create 7z archive
|
||||
run: 7z a -t7z -mx9 -bd -y fastfetch-windows-${{ matrix.arch }}${{ matrix.win7-compat-postfix }}.7z LICENSE *.dll fastfetch.exe flashfetch.exe presets
|
||||
run: 7z a -t7z -mx9 -bd -y fastfetch-windows-${{ matrix.arch }}.7z LICENSE *.dll fastfetch.exe flashfetch.exe presets
|
||||
|
||||
- name: upload true artifacts
|
||||
uses: actions/upload-artifact@v7
|
||||
with:
|
||||
name: fastfetch-windows-${{ matrix.arch }}${{ matrix.win7-compat-postfix }}
|
||||
path: ./fastfetch-windows-${{ matrix.arch }}${{ matrix.win7-compat-postfix }}.*
|
||||
name: fastfetch-windows-${{ matrix.arch }}
|
||||
path: ./fastfetch-windows-${{ matrix.arch }}.*
|
||||
overwrite: true
|
||||
|
||||
release:
|
||||
|
||||
@@ -1,3 +1,34 @@
|
||||
# 2.61.0
|
||||
|
||||
Changes:
|
||||
* Support for Windows 7 and 8 has been removed.
|
||||
* Windows 8.1 is now the oldest version supported by fastfetch.
|
||||
* The GPU module on WSL no longer relies on `DXCore`.
|
||||
* The `directx-headers` dependency is no longer required.
|
||||
* Fastfetch on Linux is now pure C; a C++ compiler is no longer required.
|
||||
* GPU type detection is now slightly less accurate, but detection speed should be slightly faster.
|
||||
* The GPU module on Windows now uses `DXCore` for more accurate GPU type detection (requires Windows 10 or later).
|
||||
* This feature is built only when `DXCore` headers are available, which requires installing `mingw-w64-<msystem>-x86_64-directx-headers` on MSYS2.
|
||||
|
||||
Features:
|
||||
* Adds a `brightness` option for color display configuration (#2238, Colors)
|
||||
* Adds support for detecting Bluetooth keyboards on Linux (#2220, Keyboard)
|
||||
* Adds support for detecting GlazeWM (WM, macOS)
|
||||
* Adds a `showEmptySlots` option to display empty memory slots on Linux (#2222, PhysicalMemory)
|
||||
* Adds marketing product name detection on Asahi Linux (Host, Linux)
|
||||
* Adds support for new M5 Mac models (Host, macOS)
|
||||
* Improves SMBIOS robustness by validating malformed data and improving error handling
|
||||
* Improves reliability when terminating child processes (Processing, Windows)
|
||||
* Improves Intel Mac support by querying SMBIOS data directly (Global, macOS)
|
||||
* Includes numerous internal cleanups and optimizations
|
||||
|
||||
Bugfixes:
|
||||
* Fixes missing memory devices on some machines (PhysicalMemory)
|
||||
* Fixes CPUCache deduplication for shared caches (#2228, CPUCache, Linux)
|
||||
* Fixes WM version reporting for niri (#2218, WM, Linux)
|
||||
* Fixes SSID decoding issues from `iw` output (Wifi, Linux)
|
||||
* Fixes the CMD code page being changed after running fastfetch on Windows (#2245, Windows)
|
||||
|
||||
# 2.60.0
|
||||
|
||||
Changes:
|
||||
|
||||
+34
-30
@@ -1,7 +1,7 @@
|
||||
cmake_minimum_required(VERSION 3.12.0) # target_link_libraries with OBJECT libs & project homepage url
|
||||
|
||||
project(fastfetch
|
||||
VERSION 2.60.0
|
||||
VERSION 2.61.0
|
||||
LANGUAGES C
|
||||
DESCRIPTION "Fast neofetch-like system information tool"
|
||||
HOMEPAGE_URL "https://github.com/fastfetch-cli/fastfetch"
|
||||
@@ -89,7 +89,6 @@ cmake_dependent_option(ENABLE_OPENCL "Enable opencl" ON "LINUX OR FreeBSD OR Ope
|
||||
cmake_dependent_option(ENABLE_FREETYPE "Enable freetype" ON "ANDROID" OFF)
|
||||
cmake_dependent_option(ENABLE_PULSE "Enable pulse" ON "LINUX OR ANDROID OR GNU" OFF)
|
||||
cmake_dependent_option(ENABLE_DDCUTIL "Enable ddcutil" ON "LINUX" OFF)
|
||||
cmake_dependent_option(ENABLE_DIRECTX_HEADERS "Enable DirectX headers for WSL" ON "LINUX" OFF)
|
||||
cmake_dependent_option(ENABLE_ELF "Enable libelf" ON "LINUX OR ANDROID OR DragonFly OR Haiku OR GNU" OFF)
|
||||
cmake_dependent_option(ENABLE_THREADS "Enable multithreading" ON "Threads_FOUND" OFF)
|
||||
|
||||
@@ -107,7 +106,7 @@ option(ENABLE_EMBEDDED_AMDGPUIDS "Embed amdgpu.ids into fastfetch, requires `pyt
|
||||
option(ENABLE_WORDEXP "Enable using of wordexp(3) if available, instead of glob(3)" ON)
|
||||
option(ENABLE_LIBZFS "Enable libzfs" ON)
|
||||
if(WIN32 AND NOT CMAKE_SYSTEM_PROCESSOR STREQUAL "aarch64")
|
||||
option(ENABLE_WIN7_COMPAT "Enable Windows 7 compatibility" OFF)
|
||||
option(ENABLE_WIN81_COMPAT "Enable legacy Windows compatibility (Windows 8.1 and later; Windows 7 unsupported)" ON)
|
||||
endif()
|
||||
if(APPLE)
|
||||
option(ENABLE_APPLE_MEMSIZE_USABLE "Use usable memory size as total memory size in Memory module, to match other systems" OFF)
|
||||
@@ -159,7 +158,7 @@ set(WARNING_FLAGS "-Wall -Wextra -Wconversion -Werror=uninitialized -Werror=retu
|
||||
set(CMAKE_C_STANDARD 11)
|
||||
set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} ${WARNING_FLAGS} -Werror=incompatible-pointer-types -Werror=implicit-function-declaration -Werror=int-conversion")
|
||||
|
||||
if(WIN32 OR Haiku OR ENABLE_DIRECTX_HEADERS)
|
||||
if(WIN32 OR Haiku)
|
||||
enable_language(CXX)
|
||||
set(CMAKE_CXX_STANDARD 17)
|
||||
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} ${WARNING_FLAGS}")
|
||||
@@ -168,8 +167,8 @@ endif()
|
||||
if(WIN32)
|
||||
set(CMAKE_EXE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS} -Wl,--tsaware -Wl,--build-id")
|
||||
|
||||
if(ENABLE_WIN7_COMPAT)
|
||||
set(CMAKE_EXE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS} -Wl,--subsystem,console,--major-os-version,6,--minor-os-version,1")
|
||||
if(ENABLE_WIN81_COMPAT)
|
||||
set(CMAKE_EXE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS} -Wl,--subsystem,console,--major-os-version,6,--minor-os-version,3")
|
||||
else()
|
||||
set(CMAKE_EXE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS} -Wl,--subsystem,console,--major-os-version,10,--minor-os-version,0")
|
||||
endif()
|
||||
@@ -305,7 +304,8 @@ if(ENABLE_EMBEDDED_PCIIDS AND NOT EXISTS "${PROJECT_BINARY_DIR}/fastfetch_pciids
|
||||
RESULT_VARIABLE PYTHON_PCIIDS_RETCODE)
|
||||
if(NOT PYTHON_PCIIDS_RETCODE EQUAL 0)
|
||||
file(REMOVE "${PROJECT_BINARY_DIR}/fastfetch_pciids.c.inc")
|
||||
message(FATAL_ERROR "Failed to generate 'fastfetch_pciids.c.inc'")
|
||||
message(WARNING "Failed to generate 'fastfetch_pciids.c.inc'")
|
||||
set(ENABLE_EMBEDDED_PCIIDS OFF)
|
||||
endif()
|
||||
else()
|
||||
message(WARNING "Python3 is not found, 'fastfetch_pciids.c.inc' will not be generated")
|
||||
@@ -325,7 +325,8 @@ if(ENABLE_EMBEDDED_AMDGPUIDS AND NOT EXISTS "${PROJECT_BINARY_DIR}/fastfetch_amd
|
||||
RESULT_VARIABLE PYTHON_AMDGPUIDS_RETCODE)
|
||||
if(NOT PYTHON_AMDGPUIDS_RETCODE EQUAL 0)
|
||||
file(REMOVE "${PROJECT_BINARY_DIR}/fastfetch_amdgpuids.c.inc")
|
||||
message(FATAL_ERROR "Failed to generate 'fastfetch_amdgpuids.c.inc'")
|
||||
message(WARNING "Failed to generate 'fastfetch_amdgpuids.c.inc'")
|
||||
set(ENABLE_EMBEDDED_AMDGPUIDS OFF)
|
||||
endif()
|
||||
else()
|
||||
message(WARNING "Python3 is not found, 'fastfetch_amdgpuids.c.inc' will not be generated")
|
||||
@@ -413,6 +414,7 @@ set(LIBFASTFETCH_SRC
|
||||
src/detection/editor/editor.c
|
||||
src/detection/font/font.c
|
||||
src/detection/gpu/gpu.c
|
||||
src/detection/host/host_mac.c
|
||||
src/detection/media/media.c
|
||||
src/detection/netio/netio.c
|
||||
src/detection/opencl/opencl.c
|
||||
@@ -559,9 +561,9 @@ if(LINUX)
|
||||
src/detection/gpu/gpu_linux.c
|
||||
src/detection/gpu/gpu_drm.c
|
||||
src/detection/gpu/gpu_pci.c
|
||||
src/detection/gpu/gpu_wsl.c
|
||||
src/detection/gtk_qt/gtk.c
|
||||
src/detection/host/host_linux.c
|
||||
src/detection/host/host_mac.c
|
||||
src/detection/icons/icons_linux.c
|
||||
src/detection/initsystem/initsystem_linux.c
|
||||
src/detection/keyboard/keyboard_linux.c
|
||||
@@ -720,7 +722,6 @@ elseif(FreeBSD)
|
||||
src/detection/gpu/gpu_pci.c
|
||||
src/detection/gtk_qt/gtk.c
|
||||
src/detection/host/host_bsd.c
|
||||
src/detection/host/host_mac.c
|
||||
src/detection/lm/lm_linux.c
|
||||
src/detection/icons/icons_linux.c
|
||||
src/detection/initsystem/initsystem_linux.c
|
||||
@@ -947,14 +948,11 @@ elseif(APPLE)
|
||||
src/common/apple/osascript.m
|
||||
src/common/apple/smc_temps.c
|
||||
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
|
||||
src/detection/btrfs/btrfs_nosupport.c
|
||||
src/detection/chassis/chassis_apple.c
|
||||
src/detection/cpu/cpu_apple.c
|
||||
src/detection/cpucache/cpucache_apple.c
|
||||
src/detection/cpuusage/cpuusage_apple.c
|
||||
@@ -962,14 +960,11 @@ elseif(APPLE)
|
||||
src/detection/disk/disk_bsd.c
|
||||
src/detection/dns/dns_apple.c
|
||||
src/detection/physicaldisk/physicaldisk_apple.c
|
||||
src/detection/physicalmemory/physicalmemory_apple.m
|
||||
src/detection/diskio/diskio_apple.c
|
||||
src/detection/displayserver/displayserver_apple.c
|
||||
src/detection/font/font_apple.m
|
||||
src/detection/gpu/gpu_apple.c
|
||||
src/detection/gpu/gpu_apple.m
|
||||
src/detection/host/host_apple.c
|
||||
src/detection/host/host_mac.c
|
||||
src/detection/icons/icons_nosupport.c
|
||||
src/detection/initsystem/initsystem_linux.c
|
||||
src/detection/keyboard/keyboard_apple.c
|
||||
@@ -1005,6 +1000,24 @@ elseif(APPLE)
|
||||
src/detection/wmtheme/wmtheme_apple.m
|
||||
src/detection/camera/camera_apple.m
|
||||
)
|
||||
# CMAKE_SYSTEM_PROCESSOR has been normalized before
|
||||
if(CMAKE_SYSTEM_PROCESSOR STREQUAL "amd64")
|
||||
list(APPEND LIBFASTFETCH_SRC
|
||||
src/detection/bios/bios_windows.c
|
||||
src/detection/board/board_windows.c
|
||||
src/detection/chassis/chassis_windows.c
|
||||
src/detection/host/host_windows.c
|
||||
src/detection/physicalmemory/physicalmemory_linux.c
|
||||
)
|
||||
else()
|
||||
list(APPEND LIBFASTFETCH_SRC
|
||||
src/detection/bios/bios_apple.c
|
||||
src/detection/board/board_apple.c
|
||||
src/detection/chassis/chassis_apple.c
|
||||
src/detection/host/host_apple.c
|
||||
src/detection/physicalmemory/physicalmemory_apple.m
|
||||
)
|
||||
endif()
|
||||
elseif(WIN32)
|
||||
list(APPEND LIBFASTFETCH_SRC
|
||||
src/common/impl/io_windows.c
|
||||
@@ -1043,7 +1056,7 @@ elseif(WIN32)
|
||||
src/detection/dns/dns_windows.c
|
||||
src/detection/font/font_windows.c
|
||||
src/detection/gpu/gpu_windows.c
|
||||
src/detection/host/host_mac.c
|
||||
src/detection/gpu/gpu_windows.cpp
|
||||
src/detection/host/host_windows.c
|
||||
src/detection/icons/icons_windows.c
|
||||
src/detection/initsystem/initsystem_nosupport.c
|
||||
@@ -1313,11 +1326,6 @@ elseif(GNU)
|
||||
)
|
||||
endif()
|
||||
|
||||
if(ENABLE_DIRECTX_HEADERS)
|
||||
message(STATUS "Enabling DirectX headers for WSL")
|
||||
list(APPEND LIBFASTFETCH_SRC src/detection/gpu/gpu_wsl.cpp)
|
||||
endif()
|
||||
|
||||
# Proprietary GPU driver APIs
|
||||
if(LINUX OR FreeBSD OR WIN32)
|
||||
list(APPEND LIBFASTFETCH_SRC src/detection/gpu/gpu_nvidia.c)
|
||||
@@ -1463,10 +1471,10 @@ if(WIN32)
|
||||
target_compile_definitions(libfastfetch PUBLIC FF_HAVE_MSVC_MSIZE=1)
|
||||
endif()
|
||||
|
||||
if(ENABLE_WIN7_COMPAT)
|
||||
target_compile_definitions(libfastfetch PUBLIC FF_WIN7_COMPAT=1)
|
||||
if(ENABLE_WIN81_COMPAT)
|
||||
target_compile_definitions(libfastfetch PUBLIC FF_WIN81_COMPAT=1)
|
||||
else()
|
||||
target_compile_definitions(libfastfetch PUBLIC FF_WIN7_COMPAT=0)
|
||||
target_compile_definitions(libfastfetch PUBLIC FF_WIN81_COMPAT=0)
|
||||
endif()
|
||||
else()
|
||||
check_function_exists(malloc_usable_size HAVE_MALLOC_USABLE_SIZE)
|
||||
@@ -1659,10 +1667,6 @@ ff_lib_enable(ELF
|
||||
"libelf"
|
||||
"libelf"
|
||||
)
|
||||
ff_lib_enable(DIRECTX_HEADERS
|
||||
"DirectX-Headers"
|
||||
"DirectX-Headers"
|
||||
)
|
||||
|
||||
if(ENABLE_THREADS)
|
||||
target_compile_definitions(libfastfetch PRIVATE FF_HAVE_THREADS=1)
|
||||
@@ -1738,7 +1742,7 @@ elseif(WIN32)
|
||||
PRIVATE "winbrand"
|
||||
PRIVATE "secur32"
|
||||
)
|
||||
if(NOT ENABLE_WIN7_COMPAT)
|
||||
if(NOT ENABLE_WIN81_COMPAT)
|
||||
target_link_libraries(libfastfetch
|
||||
PRIVATE "mincore"
|
||||
)
|
||||
|
||||
@@ -13,7 +13,9 @@
|
||||
[](https://deepwiki.com/fastfetch-cli/fastfetch)
|
||||
[](README-cn.md)
|
||||
|
||||
Fastfetch is a [neofetch](https://github.com/dylanaraps/neofetch)-like tool for fetching system information and displaying it in a visually appealing way. It is written mainly in C, with a focus on performance and customizability. Currently, it supports Linux, macOS, Windows 7+, Android, FreeBSD, OpenBSD, NetBSD, DragonFly, Haiku and SunOS (illumos, Solaris).
|
||||
Fastfetch is a [neofetch](https://github.com/dylanaraps/neofetch)-like tool for fetching system information and displaying it in a visually appealing way. It is written mainly in C, with a focus on performance and customizability. Currently, it supports Linux, macOS, Windows 8.1+, Android, FreeBSD, OpenBSD, NetBSD, DragonFly, Haiku and SunOS (illumos, Solaris).
|
||||
|
||||
> Note: Fastfetch is only actively tested on x86-64 and aarch64 platforms. It may work on other platforms but is not guaranteed to do so.
|
||||
|
||||
<img src="screenshots/example1.png" width="49%" align="left" />
|
||||
<img src="https://upload.wikimedia.org/wikipedia/commons/2/24/Transparent_Square_Tiles_Texture.png" width="49%" height="16px" align="left" />
|
||||
|
||||
+27
-3
@@ -342,7 +342,7 @@
|
||||
"type": "string"
|
||||
},
|
||||
"displayFormat": {
|
||||
"description": "Output format of the module `Display`. See Wiki for formatting syntax\n 1. {width}: Screen configured width (in pixels)\n 2. {height}: Screen configured height (in pixels)\n 3. {refresh-rate}: Screen configured refresh rate (in Hz)\n 4. {scaled-width}: Screen scaled width (in pixels)\n 5. {scaled-height}: Screen scaled height (in pixels)\n 6. {name}: Screen name\n 7. {type}: Screen type (Built-in or External)\n 8. {rotation}: Screen rotation (in degrees)\n 9. {is-primary}: True if being the primary screen\n 10. {physical-width}: Screen physical width (in millimeters)\n 11. {physical-height}: Screen physical height (in millimeters)\n 12. {inch}: Physical diagonal length in inches\n 13. {ppi}: Pixels per inch (PPI)\n 14. {bit-depth}: Bits per color channel\n 15. {hdr-enabled}: True if high dynamic range (HDR) mode is enabled\n 16. {manufacture-year}: Year of manufacturing\n 17. {manufacture-week}: Nth week of manufacturing in the year\n 18. {serial}: Serial number\n 19. {platform-api}: The platform API used when detecting the display\n 20. {hdr-compatible}: True if the display is HDR compatible\n 21. {scale-factor}: HiDPI scale factor\n 22. {preferred-width}: Screen preferred width (in pixels)\n 23. {preferred-height}: Screen preferred height (in pixels)\n 24. {preferred-refresh-rate}: Screen preferred refresh rate (in Hz)",
|
||||
"description": "Output format of the module `Display`. See Wiki for formatting syntax\n 1. {width}: Screen configured width (in pixels)\n 2. {height}: Screen configured height (in pixels)\n 3. {refresh-rate}: Screen configured refresh rate (in Hz)\n 4. {scaled-width}: Screen scaled width (in pixels)\n 5. {scaled-height}: Screen scaled height (in pixels)\n 6. {name}: Screen name\n 7. {type}: Screen type (Built-in or External)\n 8. {rotation}: Screen rotation (in degrees)\n 9. {is-primary}: True if being the primary screen\n 10. {physical-width}: Screen physical width (in millimeters)\n 11. {physical-height}: Screen physical height (in millimeters)\n 12. {inch}: Physical diagonal length in inches\n 13. {ppi}: Pixels per inch (PPI)\n 14. {bit-depth}: Bits per color channel\n 15. {hdr-enabled}: True if high dynamic range (HDR) mode is enabled\n 16. {manufacture-year}: Year of manufacturing\n 17. {manufacture-week}: Nth week of manufacturing in the year\n 18. {serial}: Serial number\n 19. {platform-api}: The platform API used when detecting the display\n 20. {hdr-compatible}: True if the display is HDR compatible\n 21. {scale-factor}: HiDPI scale factor\n 22. {preferred-width}: Screen preferred width (in pixels)\n 23. {preferred-height}: Screen preferred height (in pixels)\n 24. {preferred-refresh-rate}: Screen preferred refresh rate (in Hz)\n 25. {dpi}: DPI",
|
||||
"type": "string"
|
||||
},
|
||||
"diskFormat": {
|
||||
@@ -450,7 +450,7 @@
|
||||
"type": "string"
|
||||
},
|
||||
"physicalmemoryFormat": {
|
||||
"description": "Output format of the module `PhysicalMemory`. See Wiki for formatting syntax\n 1. {bytes}: Size (in bytes)\n 2. {size}: Size formatted\n 3. {max-speed}: Max speed (in MT/s)\n 4. {running-speed}: Running speed (in MT/s)\n 5. {type}: Type (DDR4, DDR5, etc.)\n 6. {form-factor}: Form factor (SODIMM, DIMM, etc.)\n 7. {locator}: Bank/Device Locator (BANK0/SIMM0, BANK0/SIMM1, etc.)\n 8. {vendor}: Vendor\n 9. {serial}: Serial number\n 10. {part-number}: Part number\n 11. {is-ecc-enabled}: True if ECC enabled",
|
||||
"description": "Output format of the module `PhysicalMemory`. See Wiki for formatting syntax\n 1. {bytes}: Size (in bytes)\n 2. {size}: Size formatted\n 3. {max-speed}: Max speed (in MT/s)\n 4. {running-speed}: Running speed (in MT/s)\n 5. {type}: Type (DDR4, DDR5, etc.)\n 6. {form-factor}: Form factor (SODIMM, DIMM, etc.)\n 7. {locator}: Bank/Device Locator (BANK0/SIMM0, BANK0/SIMM1, etc.)\n 8. {vendor}: Vendor\n 9. {serial}: Serial number\n 10. {part-number}: Part number\n 11. {is-ecc-enabled}: True if ECC enabled\n 12. {is-installed}: True if a memory module is installed in the slot",
|
||||
"type": "string"
|
||||
},
|
||||
"playerFormat": {
|
||||
@@ -2044,7 +2044,7 @@
|
||||
"default": 0
|
||||
},
|
||||
"block": {
|
||||
"description": "Set behavior of block printing",
|
||||
"description": "Set behavior of block printing. Only works when symbol is set to `block` or `background`",
|
||||
"type": "object",
|
||||
"additionalProperties": false,
|
||||
"properties": {
|
||||
@@ -2067,6 +2067,25 @@
|
||||
}
|
||||
}
|
||||
},
|
||||
"brightness": {
|
||||
"description": "Set the brightness of colors. Works together with the block color range to determine which colors are displayed",
|
||||
"type": "string",
|
||||
"oneOf": [
|
||||
{
|
||||
"const": "default",
|
||||
"description": "Use the default setting (block/background: 0-15, others: 8-1)"
|
||||
},
|
||||
{
|
||||
"const": "light",
|
||||
"description": "Use ANSI bright foreground colors (codes 90-97, not color indices 9-15)"
|
||||
},
|
||||
{
|
||||
"const": "normal",
|
||||
"description": "Use normal ANSI colors (30-37; standard foreground colors, corresponding to color indices 0-7)"
|
||||
}
|
||||
],
|
||||
"default": "default"
|
||||
},
|
||||
"key": {
|
||||
"$ref": "#/$defs/key"
|
||||
},
|
||||
@@ -3650,6 +3669,11 @@
|
||||
"const": "physicalmemory",
|
||||
"description": "Print system physical memory devices"
|
||||
},
|
||||
"showEmptySlots": {
|
||||
"description": "Set if uninstalled memory slots should be printed",
|
||||
"type": "boolean",
|
||||
"default": false
|
||||
},
|
||||
"key": {
|
||||
"$ref": "#/$defs/key"
|
||||
},
|
||||
|
||||
@@ -6,6 +6,9 @@ def main(amdgpu_ids_path: str):
|
||||
with open(amdgpu_ids_path, 'r') as f:
|
||||
full_text = f.read()
|
||||
|
||||
if full_text == '':
|
||||
sys.exit(f'Error: {amdgpu_ids_path} file is empty')
|
||||
|
||||
products = []
|
||||
for line in full_text.split('\n'):
|
||||
if not line or line[0] == '#' or not ',\t' in line:
|
||||
|
||||
@@ -18,6 +18,9 @@ def main(keep_vendor_list: set, pci_ids_path: str):
|
||||
with open(pci_ids_path, 'r') as f:
|
||||
full_text = f.read()
|
||||
|
||||
if full_text == '':
|
||||
sys.exit(f'Error: {pci_ids_path} file is empty')
|
||||
|
||||
dev_list_text = full_text[:full_text.rfind('\n\n\n')] # remove known classes
|
||||
for line in dev_list_text.split('\n'):
|
||||
if not line or line[0] == '#':
|
||||
|
||||
@@ -106,6 +106,15 @@ static inline void ffListClear(FFlist* list)
|
||||
list->length = 0;
|
||||
}
|
||||
|
||||
static inline void ffListReserve(FFlist* list, uint32_t newCapacity)
|
||||
{
|
||||
if (__builtin_expect(newCapacity <= list->capacity, false))
|
||||
return;
|
||||
|
||||
list->data = (uint8_t*) realloc(list->data, (size_t) newCapacity * list->elementSize);
|
||||
list->capacity = newCapacity;
|
||||
}
|
||||
|
||||
#define FF_LIST_FOR_EACH(itemType, itemVarName, listVar) \
|
||||
assert(sizeof(itemType) == (listVar).elementSize); \
|
||||
for(itemType* itemVarName = (itemType*)(listVar).data; \
|
||||
|
||||
@@ -230,6 +230,7 @@ static inline void ffStrbufInitMoveS(FFstrbuf* strbuf, char* heapStr)
|
||||
ffStrbufInitMoveNS(strbuf, (uint32_t) strlen(heapStr), heapStr);
|
||||
}
|
||||
|
||||
// Despite the name, this function resets strbuf to the initial/unallocated state
|
||||
static inline void ffStrbufDestroy(FFstrbuf* strbuf)
|
||||
{
|
||||
if(strbuf->allocated > 0)
|
||||
@@ -627,4 +628,7 @@ static inline bool ffStrbufSeparatedContainIgnCase(const FFstrbuf* strbuf, const
|
||||
return ffStrbufSeparatedContainIgnCaseNS(strbuf, comp->length, comp->chars, separator);
|
||||
}
|
||||
|
||||
// Returns true if the strbuf is modified
|
||||
bool ffStrbufDecodeHexEscapeSequences(FFstrbuf* strbuf);
|
||||
|
||||
#define FF_STRBUF_AUTO_DESTROY FFstrbuf __attribute__((__cleanup__(ffStrbufDestroy)))
|
||||
|
||||
@@ -0,0 +1,45 @@
|
||||
#pragma once
|
||||
|
||||
#include "common/FFstrbuf.h"
|
||||
|
||||
// For ffRegReadValue(s)
|
||||
// If length is 0, data will be allocated with malloc() and must be freed by the caller
|
||||
// Otherwise, data must point to a buffer of at least length bytes, and the actual length of the data will be written to length.
|
||||
typedef struct FFArgBuffer
|
||||
{
|
||||
void* data;
|
||||
uint32_t length;
|
||||
} FFArgBuffer;
|
||||
|
||||
typedef enum __attribute__((__packed__)) FFArgType
|
||||
{
|
||||
FF_ARG_TYPE_NULL = 0,
|
||||
FF_ARG_TYPE_UINT,
|
||||
FF_ARG_TYPE_UINT64,
|
||||
FF_ARG_TYPE_UINT16,
|
||||
FF_ARG_TYPE_UINT8,
|
||||
FF_ARG_TYPE_INT,
|
||||
FF_ARG_TYPE_STRING,
|
||||
FF_ARG_TYPE_STRBUF,
|
||||
FF_ARG_TYPE_FLOAT,
|
||||
FF_ARG_TYPE_DOUBLE,
|
||||
FF_ARG_TYPE_LIST,
|
||||
FF_ARG_TYPE_BOOL,
|
||||
FF_ARG_TYPE_BUFFER,
|
||||
} FFArgType;
|
||||
|
||||
#define FF_ARG(variable, var_name) { _Generic((variable), \
|
||||
uint32_t: FF_ARG_TYPE_UINT, \
|
||||
uint64_t: FF_ARG_TYPE_UINT64, \
|
||||
uint16_t: FF_ARG_TYPE_UINT16, \
|
||||
uint8_t: FF_ARG_TYPE_UINT8, \
|
||||
int32_t: FF_ARG_TYPE_INT, \
|
||||
char*: FF_ARG_TYPE_STRING, \
|
||||
const char*: FF_ARG_TYPE_STRING, \
|
||||
FFstrbuf: FF_ARG_TYPE_STRBUF, \
|
||||
float: FF_ARG_TYPE_FLOAT, \
|
||||
double: FF_ARG_TYPE_DOUBLE, \
|
||||
FFlist: FF_ARG_TYPE_LIST, \
|
||||
bool: FF_ARG_TYPE_BOOL, \
|
||||
FFArgBuffer: FF_ARG_TYPE_BUFFER \
|
||||
), _Generic((variable), char*: (variable), const char*: (variable), default: &(variable) ), (var_name) }
|
||||
+3
-1
@@ -29,5 +29,7 @@ static inline const char* ffFindFileName(const char* file)
|
||||
#define FF_DEBUG(format, ...) FF_DEBUG_PRINT(__FILE__, __LINE__, format, ##__VA_ARGS__)
|
||||
|
||||
#if _WIN32
|
||||
const char* ffDebugWin32Error(unsigned long errorCode);
|
||||
const char* ffDebugWin32Error(DWORD errorCode);
|
||||
const char* ffDebugNtStatus(NTSTATUS status);
|
||||
const char* ffDebugHResult(HRESULT hr);
|
||||
#endif
|
||||
|
||||
@@ -10,3 +10,4 @@ void ffEdidGetPhysicalResolution(const uint8_t edid[128], uint32_t* width, uint3
|
||||
void ffEdidGetPhysicalSize(const uint8_t edid[128], uint32_t* width, uint32_t* height); // in mm
|
||||
void ffEdidGetSerialAndManufactureDate(const uint8_t edid[128], uint32_t* serial, uint16_t* year, uint16_t* week);
|
||||
bool ffEdidGetHdrCompatible(const uint8_t* edid, uint32_t length);
|
||||
bool ffEdidIsValid(const uint8_t edid[128], uint32_t length);
|
||||
|
||||
+2
-33
@@ -1,41 +1,10 @@
|
||||
#pragma once
|
||||
|
||||
#include "common/FFstrbuf.h"
|
||||
|
||||
typedef enum __attribute__((__packed__)) FFformatArgType
|
||||
{
|
||||
FF_FORMAT_ARG_TYPE_NULL = 0,
|
||||
FF_FORMAT_ARG_TYPE_UINT,
|
||||
FF_FORMAT_ARG_TYPE_UINT64,
|
||||
FF_FORMAT_ARG_TYPE_UINT16,
|
||||
FF_FORMAT_ARG_TYPE_UINT8,
|
||||
FF_FORMAT_ARG_TYPE_INT,
|
||||
FF_FORMAT_ARG_TYPE_STRING,
|
||||
FF_FORMAT_ARG_TYPE_STRBUF,
|
||||
FF_FORMAT_ARG_TYPE_FLOAT,
|
||||
FF_FORMAT_ARG_TYPE_DOUBLE,
|
||||
FF_FORMAT_ARG_TYPE_LIST,
|
||||
FF_FORMAT_ARG_TYPE_BOOL
|
||||
} FFformatArgType;
|
||||
|
||||
#define FF_FORMAT_ARG(variable, var_name) { _Generic((variable), \
|
||||
uint32_t: FF_FORMAT_ARG_TYPE_UINT, \
|
||||
uint64_t: FF_FORMAT_ARG_TYPE_UINT64, \
|
||||
uint16_t: FF_FORMAT_ARG_TYPE_UINT16, \
|
||||
uint8_t: FF_FORMAT_ARG_TYPE_UINT8, \
|
||||
int32_t: FF_FORMAT_ARG_TYPE_INT, \
|
||||
char*: FF_FORMAT_ARG_TYPE_STRING, \
|
||||
const char*: FF_FORMAT_ARG_TYPE_STRING, \
|
||||
FFstrbuf: FF_FORMAT_ARG_TYPE_STRBUF, \
|
||||
float: FF_FORMAT_ARG_TYPE_FLOAT, \
|
||||
double: FF_FORMAT_ARG_TYPE_DOUBLE, \
|
||||
FFlist: FF_FORMAT_ARG_TYPE_LIST, \
|
||||
bool: FF_FORMAT_ARG_TYPE_BOOL \
|
||||
), _Generic((variable), char*: (variable), const char*: (variable), default: &(variable) ), (var_name) }
|
||||
#include "common/argType.h"
|
||||
|
||||
typedef struct FFformatarg
|
||||
{
|
||||
FFformatArgType type;
|
||||
FFArgType type;
|
||||
const void* value;
|
||||
const char* name; // argument name, must start with an alphabet
|
||||
} FFformatarg;
|
||||
|
||||
@@ -1,12 +1,12 @@
|
||||
#include "FFPlatform_private.h"
|
||||
#include "common/io.h"
|
||||
#include "common/library.h"
|
||||
#include "common/mallocHelper.h"
|
||||
#include "common/stringUtils.h"
|
||||
#include "common/windows/unicode.h"
|
||||
#include "common/windows/registry.h"
|
||||
#include "common/windows/nt.h"
|
||||
|
||||
#include <stdalign.h>
|
||||
#include <windows.h>
|
||||
#include <shlobj.h>
|
||||
#include <sddl.h>
|
||||
@@ -17,20 +17,32 @@
|
||||
static void getExePath(FFPlatform* platform)
|
||||
{
|
||||
wchar_t exePathW[MAX_PATH];
|
||||
DWORD exePathWLen = GetModuleFileNameW(NULL, exePathW, MAX_PATH);
|
||||
if (exePathWLen == 0 || exePathWLen >= MAX_PATH) return;
|
||||
|
||||
FF_AUTO_CLOSE_FD HANDLE hPath = CreateFileW(exePathW, GENERIC_READ, FILE_SHARE_READ, NULL, OPEN_EXISTING, FILE_FLAG_BACKUP_SEMANTICS, NULL);
|
||||
FF_AUTO_CLOSE_FD HANDLE hPath = CreateFileW(
|
||||
ffGetPeb()->ProcessParameters->ImagePathName.Buffer,
|
||||
GENERIC_READ,
|
||||
FILE_SHARE_READ,
|
||||
NULL,
|
||||
OPEN_EXISTING,
|
||||
FILE_FLAG_BACKUP_SEMANTICS,
|
||||
NULL);
|
||||
if (hPath != INVALID_HANDLE_VALUE)
|
||||
{
|
||||
DWORD len = GetFinalPathNameByHandleW(hPath, exePathW, MAX_PATH, FILE_NAME_OPENED);
|
||||
DWORD len = GetFinalPathNameByHandleW(hPath, exePathW, MAX_PATH, FILE_NAME_NORMALIZED);
|
||||
if (len > 0 && len < MAX_PATH)
|
||||
exePathWLen = len;
|
||||
{
|
||||
ffStrbufSetNWS(&platform->exePath, len, exePathW);
|
||||
if (ffStrbufStartsWithS(&platform->exePath, "\\\\?\\"))
|
||||
ffStrbufSubstrAfter(&platform->exePath, 3);
|
||||
}
|
||||
}
|
||||
|
||||
if (platform->exePath.length == 0)
|
||||
{
|
||||
PCUNICODE_STRING imagePathName = &ffGetPeb()->ProcessParameters->ImagePathName;
|
||||
ffStrbufSetNWS(&platform->exePath, imagePathName->Length / sizeof(wchar_t), imagePathName->Buffer);
|
||||
}
|
||||
|
||||
ffStrbufSetNWS(&platform->exePath, exePathWLen, exePathW);
|
||||
if (ffStrbufStartsWithS(&platform->exePath, "\\\\?\\"))
|
||||
ffStrbufSubstrAfter(&platform->exePath, 3);
|
||||
ffStrbufReplaceAllC(&platform->exePath, '\\', '/');
|
||||
}
|
||||
|
||||
@@ -145,30 +157,18 @@ static void getUserName(FFPlatform* platform)
|
||||
|
||||
size = ARRAY_SIZE(buffer);
|
||||
if (GetUserNameW(buffer, &size)) // GetUserNameExW(10002)?
|
||||
{
|
||||
ffStrbufSetWS(&platform->userName, buffer);
|
||||
|
||||
size = 0;
|
||||
DWORD refDomainSize = 0;
|
||||
SID_NAME_USE sidNameUse = SidTypeUnknown;
|
||||
LookupAccountNameW(NULL, buffer, NULL, &size, NULL, &refDomainSize, &sidNameUse);
|
||||
if (size > 0)
|
||||
{
|
||||
FF_AUTO_FREE PSID sid = (PSID) malloc(size);
|
||||
FF_AUTO_FREE LPWSTR refDomain = (LPWSTR) malloc(refDomainSize * sizeof(wchar_t));
|
||||
if (LookupAccountNameW(NULL, buffer, sid, &size, refDomain, &refDomainSize, &sidNameUse))
|
||||
{
|
||||
LPWSTR sidString;
|
||||
if (ConvertSidToStringSidW(sid, &sidString))
|
||||
{
|
||||
ffStrbufSetWS(&platform->sid, sidString);
|
||||
LocalFree(sidString);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
ffStrbufSetS(&platform->userName, getenv("USERNAME"));
|
||||
|
||||
alignas(TOKEN_USER) char buf[SECURITY_MAX_SID_SIZE + sizeof(TOKEN_USER)];
|
||||
if (NT_SUCCESS(NtQueryInformationToken(NtCurrentProcessToken(), TokenUser, buf, sizeof(buf), &size)))
|
||||
{
|
||||
TOKEN_USER* tokenUser = (TOKEN_USER*) buf;
|
||||
UNICODE_STRING sidString = { .Buffer = buffer, .Length = 0, .MaximumLength = sizeof(buffer) };
|
||||
if (NT_SUCCESS(RtlConvertSidToUnicodeString(&sidString, tokenUser->User.Sid, FALSE)))
|
||||
ffStrbufSetNWS(&platform->sid, sidString.Length / sizeof(wchar_t), sidString.Buffer);
|
||||
}
|
||||
}
|
||||
|
||||
static void getHostName(FFPlatform* platform)
|
||||
@@ -199,7 +199,7 @@ static void getUserShell(FFPlatform* platform)
|
||||
static const char* detectWine(void)
|
||||
{
|
||||
const char * __cdecl wine_get_version(void);
|
||||
HMODULE hntdll = GetModuleHandleW(L"ntdll.dll");
|
||||
void* hntdll = ffLibraryGetModule(L"ntdll.dll");
|
||||
if (!hntdll) return NULL;
|
||||
FF_LIBRARY_LOAD_SYMBOL_LAZY(hntdll, wine_get_version);
|
||||
if (!ffwine_get_version) return NULL;
|
||||
@@ -208,30 +208,28 @@ static const char* detectWine(void)
|
||||
|
||||
static void getSystemReleaseAndVersion(FFPlatformSysinfo* info)
|
||||
{
|
||||
FF_HKEY_AUTO_DESTROY hKey = NULL;
|
||||
FF_AUTO_CLOSE_FD HANDLE hKey = NULL;
|
||||
if(!ffRegOpenKeyForRead(HKEY_LOCAL_MACHINE, L"SOFTWARE\\Microsoft\\Windows NT\\CurrentVersion", &hKey, NULL))
|
||||
return;
|
||||
|
||||
uint32_t ubr = 0;
|
||||
ffRegReadUint(hKey, L"UBR", &ubr, NULL);
|
||||
ffRegReadValues(hKey, 2, (FFRegValueArg[]) {
|
||||
FF_ARG(ubr, L"UBR"),
|
||||
FF_ARG(info->version, L"BuildLabEx"),
|
||||
}, NULL);
|
||||
|
||||
ffStrbufAppendF(&info->release,
|
||||
PPEB_FULL peb = ffGetPeb();
|
||||
|
||||
ffStrbufSetF(&info->release,
|
||||
"%u.%u.%u.%u",
|
||||
(unsigned) SharedUserData->NtMajorVersion,
|
||||
(unsigned) SharedUserData->NtMinorVersion,
|
||||
(unsigned) SharedUserData->NtBuildNumber,
|
||||
(unsigned) peb->OSMajorVersion,
|
||||
(unsigned) peb->OSMinorVersion,
|
||||
(unsigned) peb->OSBuildNumber,
|
||||
(unsigned) ubr);
|
||||
|
||||
ffRegReadStrbuf(hKey, L"BuildLabEx", &info->version, NULL);
|
||||
|
||||
const char* wineVersion = detectWine();
|
||||
if (wineVersion)
|
||||
{
|
||||
if (instance.config.general.detectVersion)
|
||||
ffStrbufSetF(&info->name, "Wine_%s", wineVersion);
|
||||
else
|
||||
ffStrbufSetStatic(&info->name, "Wine");
|
||||
}
|
||||
ffStrbufSetF(&info->name, "Wine_%s", wineVersion);
|
||||
else
|
||||
ffStrbufSetStatic(&info->name, "WIN32_NT");
|
||||
}
|
||||
@@ -303,12 +301,7 @@ static void getSystemArchitecture(FFPlatformSysinfo* info)
|
||||
|
||||
static void getCwd(FFPlatform* platform)
|
||||
{
|
||||
#if _WIN64
|
||||
static_assert(
|
||||
offsetof(RTL_USER_PROCESS_PARAMETERS, Reserved2[5]) == 0x38,
|
||||
"CurrentDirectory should be at offset 0x38 in RTL_USER_PROCESS_PARAMETERS. Structure layout mismatch detected.");
|
||||
#endif
|
||||
PCURDIR cwd = (PCURDIR) &NtCurrentTeb()->ProcessEnvironmentBlock->ProcessParameters->Reserved2[5];
|
||||
PCURDIR cwd = &ffGetPeb()->ProcessParameters->CurrentDirectory;
|
||||
ffStrbufSetNWS(&platform->cwd, cwd->DosPath.Length / sizeof(WCHAR), cwd->DosPath.Buffer);
|
||||
ffStrbufReplaceAllC(&platform->cwd, '\\', '/');
|
||||
ffStrbufEnsureEndsWithC(&platform->cwd, '/');
|
||||
@@ -316,7 +309,7 @@ static void getCwd(FFPlatform* platform)
|
||||
|
||||
void ffPlatformInitImpl(FFPlatform* platform)
|
||||
{
|
||||
platform->pid = (uint32_t) GetCurrentProcessId();
|
||||
platform->pid = (uint32_t) (uintptr_t) ffGetTeb()->ClientId.UniqueProcess;
|
||||
getExePath(platform);
|
||||
getCwd(platform);
|
||||
getHomeDir(platform);
|
||||
|
||||
@@ -6,11 +6,7 @@
|
||||
void* ffListAdd(FFlist* list)
|
||||
{
|
||||
if(list->length == list->capacity)
|
||||
{
|
||||
list->capacity = list->capacity == 0 ? FF_LIST_DEFAULT_ALLOC : list->capacity * 2;
|
||||
// realloc(NULL, newSize) is same as malloc(newSize)
|
||||
list->data = realloc(list->data, (size_t)list->capacity * list->elementSize);
|
||||
}
|
||||
ffListReserve(list, list->capacity == 0 ? FF_LIST_DEFAULT_ALLOC : list->capacity * 2);
|
||||
|
||||
++list->length;
|
||||
return ffListGet(list, list->length - 1);
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
#include "common/FFstrbuf.h"
|
||||
#include "common/mallocHelper.h"
|
||||
#include "common/stringUtils.h"
|
||||
|
||||
#include <ctype.h>
|
||||
#include <inttypes.h>
|
||||
@@ -915,3 +916,44 @@ bool ffStrbufSeparatedContainIgnCaseNS(const FFstrbuf* strbuf, uint32_t compLeng
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
bool ffStrbufDecodeHexEscapeSequences(FFstrbuf* strbuf)
|
||||
{
|
||||
assert(strbuf);
|
||||
|
||||
if (strbuf->length < 4)
|
||||
return false;
|
||||
|
||||
// Static string must be converted first.
|
||||
assert(strbuf->allocated > 0);
|
||||
|
||||
bool changed = false;
|
||||
uint32_t read = 0;
|
||||
uint32_t write = 0;
|
||||
|
||||
while (read < strbuf->length)
|
||||
{
|
||||
if (
|
||||
read + 3 < strbuf->length &&
|
||||
strbuf->chars[read] == '\\' &&
|
||||
strbuf->chars[read + 1] == 'x'
|
||||
)
|
||||
{
|
||||
int8_t hi = ffHexCharToInt(strbuf->chars[read + 2]);
|
||||
int8_t lo = ffHexCharToInt(strbuf->chars[read + 3]);
|
||||
if (hi >= 0 && lo >= 0)
|
||||
{
|
||||
strbuf->chars[write++] = (char) ((hi << 4) | lo);
|
||||
read += 4;
|
||||
changed = true;
|
||||
continue;
|
||||
}
|
||||
}
|
||||
|
||||
strbuf->chars[write++] = strbuf->chars[read++];
|
||||
}
|
||||
|
||||
strbuf->length = write;
|
||||
strbuf->chars[write] = '\0';
|
||||
return changed;
|
||||
}
|
||||
|
||||
@@ -21,18 +21,19 @@ const char* ffBinaryExtractStrings(const char *peFile, bool (*cb)(const char *st
|
||||
if (hFile == INVALID_HANDLE_VALUE)
|
||||
return "CreateFileA() failed";
|
||||
|
||||
FF_AUTO_CLOSE_FD HANDLE hMap = CreateFileMappingW(hFile, NULL, PAGE_READONLY, 0, 0, NULL);
|
||||
if (!hMap)
|
||||
return "CreateFileMappingW() failed";
|
||||
FF_AUTO_CLOSE_FD HANDLE hSection = NULL;
|
||||
if (!NT_SUCCESS(NtCreateSection(&hSection, SECTION_MAP_READ, NULL, NULL, PAGE_READONLY, SEC_COMMIT, hFile)))
|
||||
return "NtCreateSection() failed";
|
||||
|
||||
void* base = MapViewOfFile(hMap, FILE_MAP_READ, 0, 0, 0);
|
||||
if (!base)
|
||||
return "MapViewOfFile() failed";
|
||||
PVOID base = NULL;
|
||||
SIZE_T viewSize = 0;
|
||||
if (!NT_SUCCESS(NtMapViewOfSection(hSection, NtCurrentProcess(), &base, 0, 0, NULL, &viewSize, ViewUnmap, 0, PAGE_READONLY)))
|
||||
return "NtMapViewOfSection() failed";
|
||||
|
||||
PIMAGE_NT_HEADERS ntHeaders = RtlImageNtHeader(base);
|
||||
if (!ntHeaders)
|
||||
{
|
||||
UnmapViewOfFile(base);
|
||||
NtUnmapViewOfSection(NtCurrentProcess(), base);
|
||||
return "RtlImageNtHeader() failed";
|
||||
}
|
||||
|
||||
@@ -61,6 +62,6 @@ const char* ffBinaryExtractStrings(const char *peFile, bool (*cb)(const char *st
|
||||
}
|
||||
}
|
||||
|
||||
UnmapViewOfFile(base);
|
||||
NtUnmapViewOfSection(NtCurrentProcess(), base);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
@@ -10,7 +10,7 @@ const char* ffDebugWin32Error(DWORD errorCode)
|
||||
FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_IGNORE_INSERTS,
|
||||
NULL,
|
||||
(DWORD) errorCode,
|
||||
MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT),
|
||||
0,
|
||||
buffer,
|
||||
sizeof(buffer),
|
||||
NULL);
|
||||
@@ -19,10 +19,32 @@ const char* ffDebugWin32Error(DWORD errorCode)
|
||||
snprintf(buffer, sizeof(buffer), "Unknown error code (%lu)", errorCode);
|
||||
} else {
|
||||
// Remove trailing newline
|
||||
if (buffer[len - 1] == '\n') buffer[len - 1] = '\0';
|
||||
if (buffer[len - 2] == '\r') buffer[len - 2] = '\0';
|
||||
snprintf(buffer + len - 2, sizeof(buffer) - len + 2, " (%lu)", errorCode);
|
||||
while (len > 0 && (buffer[len - 1] == '\r' || buffer[len - 1] == '\n')) {
|
||||
buffer[--len] = '\0';
|
||||
}
|
||||
snprintf(buffer + len, sizeof(buffer) - len + 2, " (%lu)", errorCode);
|
||||
}
|
||||
|
||||
return buffer;
|
||||
}
|
||||
|
||||
const char* ffDebugNtStatus(NTSTATUS status)
|
||||
{
|
||||
return ffDebugWin32Error(RtlNtStatusToDosError(status));
|
||||
}
|
||||
|
||||
static inline DWORD HRESULTToWin32Error(HRESULT hr)
|
||||
{
|
||||
if (SUCCEEDED(hr))
|
||||
return ERROR_SUCCESS;
|
||||
|
||||
if (HRESULT_FACILITY(hr) == FACILITY_WIN32)
|
||||
return HRESULT_CODE(hr);
|
||||
|
||||
return ERROR_INTERNAL_ERROR;
|
||||
}
|
||||
|
||||
const char* ffDebugHResult(HRESULT hr)
|
||||
{
|
||||
return ffDebugWin32Error(HRESULTToWin32Error(hr));
|
||||
}
|
||||
|
||||
@@ -128,3 +128,16 @@ bool ffEdidGetHdrCompatible(const uint8_t* edid, uint32_t length)
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
bool ffEdidIsValid(const uint8_t edid[128], uint32_t length)
|
||||
{
|
||||
if (length < 128 || length % 128 != 0) return false;
|
||||
|
||||
static const uint8_t edidHeader[] = { 0x00, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x00 };
|
||||
if (memcmp(edid, edidHeader, sizeof(edidHeader)) != 0) return false;
|
||||
|
||||
uint8_t sum = 0;
|
||||
for (uint32_t i = 0; i < 128; i++) sum += edid[i];
|
||||
|
||||
return sum == 0;
|
||||
}
|
||||
|
||||
+29
-22
@@ -10,37 +10,37 @@ void ffFormatAppendFormatArg(FFstrbuf* buffer, const FFformatarg* formatarg)
|
||||
{
|
||||
switch(formatarg->type)
|
||||
{
|
||||
case FF_FORMAT_ARG_TYPE_INT:
|
||||
case FF_ARG_TYPE_INT:
|
||||
ffStrbufAppendSInt(buffer, *(int32_t*)formatarg->value);
|
||||
break;
|
||||
case FF_FORMAT_ARG_TYPE_UINT:
|
||||
case FF_ARG_TYPE_UINT:
|
||||
ffStrbufAppendUInt(buffer, *(uint32_t*)formatarg->value);
|
||||
break;
|
||||
case FF_FORMAT_ARG_TYPE_UINT64:
|
||||
case FF_ARG_TYPE_UINT64:
|
||||
ffStrbufAppendUInt(buffer, *(uint64_t*)formatarg->value);
|
||||
break;
|
||||
case FF_FORMAT_ARG_TYPE_UINT16:
|
||||
case FF_ARG_TYPE_UINT16:
|
||||
ffStrbufAppendUInt(buffer, *(uint16_t*)formatarg->value);
|
||||
break;
|
||||
case FF_FORMAT_ARG_TYPE_UINT8:
|
||||
case FF_ARG_TYPE_UINT8:
|
||||
ffStrbufAppendUInt(buffer, *(uint8_t*)formatarg->value);
|
||||
break;
|
||||
case FF_FORMAT_ARG_TYPE_STRING:
|
||||
case FF_ARG_TYPE_STRING:
|
||||
ffStrbufAppendS(buffer, (const char*)formatarg->value);
|
||||
break;
|
||||
case FF_FORMAT_ARG_TYPE_STRBUF:
|
||||
case FF_ARG_TYPE_STRBUF:
|
||||
ffStrbufAppend(buffer, (const FFstrbuf*)formatarg->value);
|
||||
break;
|
||||
case FF_FORMAT_ARG_TYPE_FLOAT:
|
||||
case FF_ARG_TYPE_FLOAT:
|
||||
ffStrbufAppendDouble(buffer, *(float*)formatarg->value, instance.config.display.fractionNdigits, instance.config.display.fractionTrailingZeros != FF_FRACTION_TRAILING_ZEROS_TYPE_NEVER);
|
||||
break;
|
||||
case FF_FORMAT_ARG_TYPE_DOUBLE:
|
||||
case FF_ARG_TYPE_DOUBLE:
|
||||
ffStrbufAppendDouble(buffer, *(double*)formatarg->value, instance.config.display.fractionNdigits, instance.config.display.fractionTrailingZeros != FF_FRACTION_TRAILING_ZEROS_TYPE_NEVER);
|
||||
break;
|
||||
case FF_FORMAT_ARG_TYPE_BOOL:
|
||||
case FF_ARG_TYPE_BOOL:
|
||||
ffStrbufAppendS(buffer, *(bool*)formatarg->value ? "true" : "false");
|
||||
break;
|
||||
case FF_FORMAT_ARG_TYPE_LIST:
|
||||
case FF_ARG_TYPE_LIST:
|
||||
{
|
||||
const FFlist* list = (const FFlist*) formatarg->value;
|
||||
for(uint32_t i = 0; i < list->length; i++)
|
||||
@@ -51,8 +51,15 @@ void ffFormatAppendFormatArg(FFstrbuf* buffer, const FFformatarg* formatarg)
|
||||
}
|
||||
break;
|
||||
}
|
||||
case FF_ARG_TYPE_BUFFER:
|
||||
{
|
||||
// Placeholder for binary data, just print the size for now
|
||||
const FFArgBuffer* argBuffer = (const FFArgBuffer*) formatarg->value;
|
||||
ffStrbufAppendF(buffer, "buffer(%u bytes)", argBuffer->length);
|
||||
break;
|
||||
}
|
||||
default:
|
||||
if(formatarg->type != FF_FORMAT_ARG_TYPE_NULL)
|
||||
if(formatarg->type != FF_ARG_TYPE_NULL)
|
||||
fprintf(stderr, "Error: format string \"%s\": argument is not implemented: %i\n", buffer->chars, formatarg->type);
|
||||
break;
|
||||
}
|
||||
@@ -107,16 +114,16 @@ static inline void appendInvalidPlaceholder(FFstrbuf* buffer, const char* start,
|
||||
static inline bool formatArgSet(const FFformatarg* arg)
|
||||
{
|
||||
return arg->value != NULL && (
|
||||
(arg->type == FF_FORMAT_ARG_TYPE_DOUBLE && *(double*)arg->value > 0.0) ||
|
||||
(arg->type == FF_FORMAT_ARG_TYPE_FLOAT && *(float*)arg->value > 0.0) ||
|
||||
(arg->type == FF_FORMAT_ARG_TYPE_INT && *(int*)arg->value > 0) ||
|
||||
(arg->type == FF_FORMAT_ARG_TYPE_STRBUF && ((FFstrbuf*)arg->value)->length > 0) ||
|
||||
(arg->type == FF_FORMAT_ARG_TYPE_STRING && ffStrSet((char*)arg->value)) ||
|
||||
(arg->type == FF_FORMAT_ARG_TYPE_UINT8 && *(uint8_t*)arg->value > 0) ||
|
||||
(arg->type == FF_FORMAT_ARG_TYPE_UINT16 && *(uint16_t*)arg->value > 0) ||
|
||||
(arg->type == FF_FORMAT_ARG_TYPE_UINT && *(uint32_t*)arg->value > 0) ||
|
||||
(arg->type == FF_FORMAT_ARG_TYPE_BOOL && *(bool*)arg->value) ||
|
||||
(arg->type == FF_FORMAT_ARG_TYPE_LIST && ((FFlist*)arg->value)->length > 0)
|
||||
(arg->type == FF_ARG_TYPE_DOUBLE && *(double*)arg->value > 0.0) ||
|
||||
(arg->type == FF_ARG_TYPE_FLOAT && *(float*)arg->value > 0.0) ||
|
||||
(arg->type == FF_ARG_TYPE_INT && *(int*)arg->value > 0) ||
|
||||
(arg->type == FF_ARG_TYPE_STRBUF && ((FFstrbuf*)arg->value)->length > 0) ||
|
||||
(arg->type == FF_ARG_TYPE_STRING && ffStrSet((char*)arg->value)) ||
|
||||
(arg->type == FF_ARG_TYPE_UINT8 && *(uint8_t*)arg->value > 0) ||
|
||||
(arg->type == FF_ARG_TYPE_UINT16 && *(uint16_t*)arg->value > 0) ||
|
||||
(arg->type == FF_ARG_TYPE_UINT && *(uint32_t*)arg->value > 0) ||
|
||||
(arg->type == FF_ARG_TYPE_BOOL && *(bool*)arg->value) ||
|
||||
(arg->type == FF_ARG_TYPE_LIST && ((FFlist*)arg->value)->length > 0)
|
||||
);
|
||||
}
|
||||
|
||||
|
||||
+19
-13
@@ -55,12 +55,15 @@ void ffInitInstance(void)
|
||||
setlocale(LC_TIME, "");
|
||||
#endif
|
||||
|
||||
initState(&instance.state);
|
||||
defaultConfig();
|
||||
initState(&instance.state);
|
||||
}
|
||||
|
||||
static volatile bool ffDisableLinewrap = true;
|
||||
static volatile bool ffHideCursor = true;
|
||||
#if _WIN32
|
||||
static volatile UINT oldCp = CP_UTF8;
|
||||
#endif
|
||||
|
||||
static void resetConsole(void)
|
||||
{
|
||||
@@ -70,12 +73,15 @@ static void resetConsole(void)
|
||||
if(ffHideCursor)
|
||||
fputs("\033[?25h", stdout);
|
||||
|
||||
#if defined(_WIN32)
|
||||
fflush(stdout);
|
||||
#endif
|
||||
|
||||
if(instance.state.dynamicInterval > 0)
|
||||
fputs("\033[?1049l", stdout); // Disable alternate buffer
|
||||
|
||||
#if defined(_WIN32)
|
||||
fflush(stdout);
|
||||
|
||||
if(oldCp != CP_UTF8)
|
||||
SetConsoleOutputCP(oldCp);
|
||||
#endif
|
||||
}
|
||||
|
||||
#ifdef _WIN32
|
||||
@@ -106,9 +112,12 @@ void ffStart(void)
|
||||
SetConsoleCtrlHandler(consoleHandler, TRUE);
|
||||
HANDLE hStdout = GetStdHandle(STD_OUTPUT_HANDLE);
|
||||
DWORD mode = 0;
|
||||
GetConsoleMode(hStdout, &mode);
|
||||
SetConsoleMode(hStdout, mode | ENABLE_PROCESSED_OUTPUT | ENABLE_VIRTUAL_TERMINAL_PROCESSING);
|
||||
SetConsoleOutputCP(CP_UTF8);
|
||||
if (GetConsoleMode(hStdout, &mode))
|
||||
{
|
||||
SetConsoleMode(hStdout, mode | ENABLE_PROCESSED_OUTPUT | ENABLE_VIRTUAL_TERMINAL_PROCESSING);
|
||||
oldCp = GetConsoleOutputCP();
|
||||
if (oldCp != CP_UTF8) SetConsoleOutputCP(CP_UTF8);
|
||||
}
|
||||
#else
|
||||
if (instance.config.display.noBuffer) setvbuf(stdout, NULL, _IONBF, 0);
|
||||
struct sigaction action = { .sa_handler = exitSignalHandler };
|
||||
@@ -237,9 +246,6 @@ void ffListFeatures(void)
|
||||
#if FF_HAVE_LIBZFS
|
||||
"libzfs\n"
|
||||
#endif
|
||||
#if FF_HAVE_DIRECTX_HEADERS
|
||||
"Directx Headers\n"
|
||||
#endif
|
||||
#if FF_USE_SYSTEM_YYJSON
|
||||
"System yyjson\n"
|
||||
#endif
|
||||
@@ -252,8 +258,8 @@ void ffListFeatures(void)
|
||||
#if FF_HAVE_EMBEDDED_PCIIDS
|
||||
"Embedded pciids\n"
|
||||
#endif
|
||||
#if FF_WIN7_COMPAT
|
||||
"Windows 7 Compatibility\n"
|
||||
#if FF_WIN81_COMPAT
|
||||
"Windows 8.1 Compatibility\n"
|
||||
#endif
|
||||
#if FF_APPLE_MEMSIZE_USABLE
|
||||
"Apple memsize_usable\n"
|
||||
|
||||
@@ -97,42 +97,6 @@ bool ffAppendFDBuffer(int fd, FFstrbuf* buffer)
|
||||
return buffer->length > 0;
|
||||
}
|
||||
|
||||
ssize_t ffReadFileData(const char* fileName, size_t dataSize, void* data)
|
||||
{
|
||||
int FF_AUTO_CLOSE_FD fd = open(fileName, O_RDONLY | O_CLOEXEC);
|
||||
if(fd == -1)
|
||||
return -1;
|
||||
|
||||
return ffReadFDData(fd, dataSize, data);
|
||||
}
|
||||
|
||||
ssize_t ffReadFileDataRelative(int dfd, const char* fileName, size_t dataSize, void* data)
|
||||
{
|
||||
int FF_AUTO_CLOSE_FD fd = openat(dfd, fileName, O_RDONLY | O_CLOEXEC);
|
||||
if(fd == -1)
|
||||
return -1;
|
||||
|
||||
return ffReadFDData(fd, dataSize, data);
|
||||
}
|
||||
|
||||
bool ffAppendFileBuffer(const char* fileName, FFstrbuf* buffer)
|
||||
{
|
||||
int FF_AUTO_CLOSE_FD fd = open(fileName, O_RDONLY | O_CLOEXEC);
|
||||
if(fd == -1)
|
||||
return false;
|
||||
|
||||
return ffAppendFDBuffer(fd, buffer);
|
||||
}
|
||||
|
||||
bool ffAppendFileBufferRelative(int dfd, const char* fileName, FFstrbuf* buffer)
|
||||
{
|
||||
int FF_AUTO_CLOSE_FD fd = openat(dfd, fileName, O_RDONLY | O_CLOEXEC);
|
||||
if(fd == -1)
|
||||
return false;
|
||||
|
||||
return ffAppendFDBuffer(fd, buffer);
|
||||
}
|
||||
|
||||
bool ffPathExpandEnv(const char* in, FFstrbuf* out)
|
||||
{
|
||||
bool result = false;
|
||||
|
||||
+179
-68
@@ -1,32 +1,172 @@
|
||||
#include "common/io.h"
|
||||
#include "fastfetch.h"
|
||||
#include "common/io.h"
|
||||
#include "common/stringUtils.h"
|
||||
#include "common/windows/nt.h"
|
||||
#include "common/windows/unicode.h"
|
||||
|
||||
#include <windows.h>
|
||||
#include "common/windows/nt.h"
|
||||
#include <ntstatus.h>
|
||||
|
||||
static void createSubfolders(const char* fileName)
|
||||
static bool createSubfolders(wchar_t* fileName)
|
||||
{
|
||||
FF_STRBUF_AUTO_DESTROY path = ffStrbufCreate();
|
||||
char *token = NULL;
|
||||
while((token = strchr(fileName, '/')) != NULL)
|
||||
HANDLE hRoot = ffGetPeb()->ProcessParameters->CurrentDirectory.Handle;
|
||||
bool closeRoot = false;
|
||||
wchar_t* ptr = fileName;
|
||||
|
||||
// Absolute drive path: C:\...
|
||||
if (ffCharIsEnglishAlphabet((char)ptr[0]) && ptr[1] == L':' && ptr[2] == L'\\')
|
||||
{
|
||||
ffStrbufAppendNS(&path, (uint32_t)(token - fileName + 1), fileName);
|
||||
CreateDirectoryA(path.chars, NULL);
|
||||
fileName = token + 1;
|
||||
wchar_t saved = ptr[3];
|
||||
ptr[3] = L'\0';
|
||||
|
||||
hRoot = CreateFileW(
|
||||
fileName,
|
||||
FILE_LIST_DIRECTORY | FILE_TRAVERSE | SYNCHRONIZE,
|
||||
FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE,
|
||||
NULL,
|
||||
OPEN_EXISTING,
|
||||
FILE_DIRECTORY_FILE | FILE_SYNCHRONOUS_IO_NONALERT | FILE_FLAG_BACKUP_SEMANTICS,
|
||||
NULL
|
||||
);
|
||||
|
||||
ptr[3] = saved;
|
||||
if (hRoot == INVALID_HANDLE_VALUE)
|
||||
return false;
|
||||
|
||||
closeRoot = true;
|
||||
ptr += 3; // skip "C:\"
|
||||
}
|
||||
// UNC path: \\server\share\...
|
||||
else if (ptr[0] == L'\\' && ptr[1] == L'\\')
|
||||
{
|
||||
wchar_t* serverEnd = wcschr(ptr + 2, L'\\');
|
||||
if (serverEnd == NULL)
|
||||
return false;
|
||||
|
||||
wchar_t* shareEnd = wcschr(serverEnd + 1, L'\\');
|
||||
if (shareEnd == NULL)
|
||||
return true; // no parent subfolder exists before file name
|
||||
|
||||
wchar_t saved = *shareEnd;
|
||||
*shareEnd = L'\0';
|
||||
|
||||
hRoot = CreateFileW(
|
||||
fileName,
|
||||
FILE_LIST_DIRECTORY | FILE_TRAVERSE | SYNCHRONIZE,
|
||||
FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE,
|
||||
NULL,
|
||||
OPEN_EXISTING,
|
||||
FILE_DIRECTORY_FILE | FILE_SYNCHRONOUS_IO_NONALERT | FILE_FLAG_BACKUP_SEMANTICS,
|
||||
NULL
|
||||
);
|
||||
|
||||
*shareEnd = saved;
|
||||
if (hRoot == INVALID_HANDLE_VALUE)
|
||||
return false;
|
||||
|
||||
closeRoot = true;
|
||||
ptr = shareEnd + 1; // first component under share
|
||||
}
|
||||
// Rooted path on current drive: \foo\bar
|
||||
else if (ptr[0] == L'\\')
|
||||
{
|
||||
UNICODE_STRING* dosPath = &ffGetPeb()->ProcessParameters->CurrentDirectory.DosPath;
|
||||
wchar_t driveRoot[] = { dosPath->Buffer[0], L':', L'\\', L'\0' };
|
||||
hRoot = CreateFileW(
|
||||
driveRoot,
|
||||
FILE_LIST_DIRECTORY | FILE_TRAVERSE | SYNCHRONIZE,
|
||||
FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE,
|
||||
NULL,
|
||||
OPEN_EXISTING,
|
||||
FILE_DIRECTORY_FILE | FILE_SYNCHRONOUS_IO_NONALERT | FILE_FLAG_BACKUP_SEMANTICS,
|
||||
NULL
|
||||
);
|
||||
if (hRoot == INVALID_HANDLE_VALUE)
|
||||
return false;
|
||||
closeRoot = true;
|
||||
ptr++; // skip leading '\'
|
||||
}
|
||||
|
||||
while (true)
|
||||
{
|
||||
wchar_t* token = wcschr(ptr, L'\\');
|
||||
if (token == NULL)
|
||||
break;
|
||||
|
||||
// Skip empty path segments caused by duplicated '\'
|
||||
if (token == ptr)
|
||||
{
|
||||
ptr = token + 1;
|
||||
continue;
|
||||
}
|
||||
|
||||
HANDLE hNew = INVALID_HANDLE_VALUE;
|
||||
IO_STATUS_BLOCK iosb = {};
|
||||
|
||||
NTSTATUS status = NtCreateFile(
|
||||
&hNew,
|
||||
FILE_LIST_DIRECTORY | FILE_TRAVERSE | SYNCHRONIZE,
|
||||
&(OBJECT_ATTRIBUTES) {
|
||||
.Length = sizeof(OBJECT_ATTRIBUTES),
|
||||
.RootDirectory = hRoot,
|
||||
.ObjectName = &(UNICODE_STRING) {
|
||||
.Buffer = ptr,
|
||||
.Length = (USHORT)((USHORT)(token - ptr) * sizeof(wchar_t)),
|
||||
.MaximumLength = (USHORT)((USHORT)(token - ptr) * sizeof(wchar_t)),
|
||||
},
|
||||
.Attributes = OBJ_CASE_INSENSITIVE,
|
||||
},
|
||||
&iosb,
|
||||
NULL,
|
||||
FILE_ATTRIBUTE_NORMAL,
|
||||
FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE,
|
||||
FILE_OPEN_IF,
|
||||
FILE_DIRECTORY_FILE | FILE_SYNCHRONOUS_IO_NONALERT,
|
||||
NULL,
|
||||
0
|
||||
);
|
||||
|
||||
if (!NT_SUCCESS(status))
|
||||
{
|
||||
if (closeRoot && hRoot != INVALID_HANDLE_VALUE)
|
||||
NtClose(hRoot);
|
||||
return false;
|
||||
}
|
||||
|
||||
if (closeRoot && hRoot != INVALID_HANDLE_VALUE)
|
||||
NtClose(hRoot);
|
||||
hRoot = hNew;
|
||||
closeRoot = true;
|
||||
|
||||
ptr = token + 1;
|
||||
}
|
||||
|
||||
if (closeRoot && hRoot != INVALID_HANDLE_VALUE)
|
||||
NtClose(hRoot);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool ffWriteFileData(const char* fileName, size_t dataSize, const void* data)
|
||||
{
|
||||
HANDLE FF_AUTO_CLOSE_FD handle = CreateFileA(fileName, GENERIC_WRITE, FILE_SHARE_WRITE, NULL, CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, NULL);
|
||||
wchar_t fileNameW[MAX_PATH];
|
||||
ULONG len = 0;
|
||||
if (!NT_SUCCESS(RtlUTF8ToUnicodeN(fileNameW, (ULONG) sizeof(fileNameW), &len, fileName, (ULONG)strlen(fileName) + 1)))
|
||||
return false;
|
||||
|
||||
for (ULONG i = 0; i < len / sizeof(wchar_t); ++i)
|
||||
{
|
||||
if (fileNameW[i] == L'/')
|
||||
fileNameW[i] = L'\\';
|
||||
}
|
||||
|
||||
HANDLE FF_AUTO_CLOSE_FD handle = CreateFileW(fileNameW, GENERIC_WRITE, FILE_SHARE_WRITE, NULL, CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, NULL);
|
||||
if (handle == INVALID_HANDLE_VALUE)
|
||||
{
|
||||
if (GetLastError() == ERROR_PATH_NOT_FOUND)
|
||||
{
|
||||
createSubfolders(fileName);
|
||||
handle = CreateFileA(fileName, GENERIC_WRITE, FILE_SHARE_WRITE, NULL, CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, NULL);
|
||||
if (!createSubfolders(fileNameW))
|
||||
return false;
|
||||
handle = CreateFileW(fileNameW, GENERIC_WRITE, FILE_SHARE_WRITE, NULL, CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, NULL);
|
||||
if (handle == INVALID_HANDLE_VALUE)
|
||||
return false;
|
||||
}
|
||||
@@ -70,12 +210,13 @@ static inline void readUntilEOF(HANDLE handle, FFstrbuf* buffer)
|
||||
|
||||
bool ffAppendFDBuffer(HANDLE handle, FFstrbuf* buffer)
|
||||
{
|
||||
LARGE_INTEGER fileSize;
|
||||
if(!GetFileSizeEx(handle, &fileSize))
|
||||
fileSize.QuadPart = 0;
|
||||
FILE_STANDARD_INFORMATION fileInfo;
|
||||
IO_STATUS_BLOCK iosb;
|
||||
if(!NT_SUCCESS(NtQueryInformationFile(handle, &iosb, &fileInfo, sizeof(fileInfo), FileStandardInformation)))
|
||||
fileInfo.EndOfFile.QuadPart = 0;
|
||||
|
||||
if (fileSize.QuadPart > 0)
|
||||
readWithLength(handle, buffer, (uint32_t)fileSize.QuadPart);
|
||||
if (fileInfo.EndOfFile.QuadPart > 0)
|
||||
readWithLength(handle, buffer, (uint32_t)fileInfo.EndOfFile.QuadPart);
|
||||
else
|
||||
readUntilEOF(handle, buffer);
|
||||
|
||||
@@ -84,24 +225,6 @@ bool ffAppendFDBuffer(HANDLE handle, FFstrbuf* buffer)
|
||||
return buffer->length > 0;
|
||||
}
|
||||
|
||||
ssize_t ffReadFileData(const char* fileName, size_t dataSize, void* data)
|
||||
{
|
||||
HANDLE FF_AUTO_CLOSE_FD handle = CreateFileA(fileName, GENERIC_READ, FILE_SHARE_READ, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL);
|
||||
if(handle == INVALID_HANDLE_VALUE)
|
||||
return -1;
|
||||
|
||||
return ffReadFDData(handle, dataSize, data);
|
||||
}
|
||||
|
||||
bool ffAppendFileBuffer(const char* fileName, FFstrbuf* buffer)
|
||||
{
|
||||
HANDLE FF_AUTO_CLOSE_FD handle = CreateFileA(fileName, GENERIC_READ, FILE_SHARE_READ, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL);
|
||||
if(handle == INVALID_HANDLE_VALUE)
|
||||
return false;
|
||||
|
||||
return ffAppendFDBuffer(handle, buffer);
|
||||
}
|
||||
|
||||
HANDLE openatW(HANDLE dfd, const wchar_t* fileName, uint16_t fileNameLen, bool directory)
|
||||
{
|
||||
assert(fileNameLen <= 0x7FFF);
|
||||
@@ -128,38 +251,22 @@ HANDLE openatW(HANDLE dfd, const wchar_t* fileName, uint16_t fileNameLen, bool d
|
||||
return hFile;
|
||||
}
|
||||
|
||||
HANDLE openat(HANDLE dfd, const char* fileName, bool directory)
|
||||
HANDLE openat(HANDLE dfd, const char* fileName, int oflag)
|
||||
{
|
||||
wchar_t fileNameW[MAX_PATH];
|
||||
int len = MultiByteToWideChar(CP_UTF8, MB_ERR_INVALID_CHARS, fileName, -1, fileNameW, ARRAY_SIZE(fileNameW));
|
||||
if (len == 0) return INVALID_HANDLE_VALUE;
|
||||
ULONG len;
|
||||
if (!NT_SUCCESS(RtlUTF8ToUnicodeN(fileNameW, (ULONG) sizeof(fileNameW), &len, fileName, (ULONG)strlen(fileName) + 1)))
|
||||
return INVALID_HANDLE_VALUE;
|
||||
// Implies `fileNameW[len] = L'\0';` and `len` includes the null terminator
|
||||
len /= sizeof(wchar_t); // convert from bytes to characters
|
||||
|
||||
for (int i = 0; i < len - 1; ++i)
|
||||
for (uint32_t i = 0; i < len - 1; ++i)
|
||||
{
|
||||
if (fileNameW[i] == L'/')
|
||||
fileNameW[i] = L'\\';
|
||||
}
|
||||
|
||||
return openatW(dfd, fileNameW, (uint16_t)(len - 1), directory);
|
||||
}
|
||||
|
||||
bool ffAppendFileBufferRelative(HANDLE dfd, const char* fileName, FFstrbuf* buffer)
|
||||
{
|
||||
HANDLE FF_AUTO_CLOSE_FD fd = openat(dfd, fileName, false);
|
||||
if(fd == INVALID_HANDLE_VALUE)
|
||||
return false;
|
||||
|
||||
return ffAppendFDBuffer(fd, buffer);
|
||||
}
|
||||
|
||||
ssize_t ffReadFileDataRelative(HANDLE dfd, const char* fileName, size_t dataSize, void* data)
|
||||
{
|
||||
HANDLE FF_AUTO_CLOSE_FD fd = openat(dfd, fileName, false);
|
||||
if(fd == INVALID_HANDLE_VALUE)
|
||||
return -1;
|
||||
|
||||
return ffReadFDData(fd, dataSize, data);
|
||||
return openatW(dfd, fileNameW, (uint16_t)(len - 1), !!(oflag & O_DIRECTORY));
|
||||
}
|
||||
|
||||
bool ffPathExpandEnv(const char* in, FFstrbuf* out)
|
||||
@@ -172,13 +279,17 @@ bool ffPathExpandEnv(const char* in, FFstrbuf* out)
|
||||
}
|
||||
}
|
||||
|
||||
DWORD length = ExpandEnvironmentStringsA(in, NULL, 0);
|
||||
if (length <= 1) return false;
|
||||
wchar_t pathInW[MAX_PATH], pathOutW[MAX_PATH];
|
||||
ULONG len = (ULONG) strlen(in);
|
||||
if (!NT_SUCCESS(RtlUTF8ToUnicodeN(pathInW, (ULONG) sizeof(pathInW), &len, in, len)))
|
||||
return false;
|
||||
len /= sizeof(wchar_t); // convert from bytes to characters
|
||||
|
||||
ffStrbufClear(out);
|
||||
ffStrbufEnsureFree(out, (uint32_t)length);
|
||||
ExpandEnvironmentStringsA(in, out->chars, length);
|
||||
out->length = (uint32_t)length - 1;
|
||||
size_t outLen; // in characters, including null terminator
|
||||
if (!NT_SUCCESS(RtlExpandEnvironmentStrings(NULL, pathInW, len, pathOutW, ARRAY_SIZE(pathOutW), &outLen)))
|
||||
return false;
|
||||
|
||||
ffStrbufSetNWS(out, (uint32_t) outLen - 1, pathOutW);
|
||||
return true;
|
||||
}
|
||||
|
||||
@@ -308,10 +419,10 @@ const char* ffGetTerminalResponse(const char* request, int nParams, const char*
|
||||
|
||||
while (true)
|
||||
{
|
||||
if (WaitForSingleObjectEx(hInput, FF_IO_TERM_RESP_WAIT_MS, TRUE) != WAIT_OBJECT_0)
|
||||
if (NtWaitForSingleObject(hInput, FALSE, &(LARGE_INTEGER) { .QuadPart = (int64_t) FF_IO_TERM_RESP_WAIT_MS * -10000 }) != STATUS_WAIT_0)
|
||||
{
|
||||
SetConsoleMode(hInput, inputMode);
|
||||
return "WaitForSingleObject() failed or timeout";
|
||||
return "NtWaitForSingleObject() failed or timeout";
|
||||
}
|
||||
|
||||
// Ignore all unexpected input events
|
||||
@@ -350,7 +461,7 @@ const char* ffGetTerminalResponse(const char* request, int nParams, const char*
|
||||
|
||||
va_list cargs;
|
||||
va_copy(cargs, args);
|
||||
int ret = vsscanf(buffer, format, args);
|
||||
int ret = vsscanf(buffer, format, cargs);
|
||||
va_end(cargs);
|
||||
|
||||
if (ret <= 0)
|
||||
|
||||
@@ -1,6 +1,13 @@
|
||||
#include "fastfetch.h"
|
||||
#include "common/library.h"
|
||||
|
||||
#if _WIN32
|
||||
#include "common/debug.h"
|
||||
#include "common/windows/nt.h"
|
||||
#include <errno.h>
|
||||
#include <ntstatus.h>
|
||||
#endif
|
||||
|
||||
#ifndef FF_DISABLE_DLOPEN
|
||||
|
||||
#include <stdarg.h>
|
||||
@@ -36,7 +43,7 @@ static void* libraryLoad(const char* path, int maxVersion)
|
||||
if (pathLen == instance.state.platform.exePath.length)
|
||||
return result;
|
||||
|
||||
char absPath[MAX_PATH + 1];
|
||||
char absPath[MAX_PATH * 2];
|
||||
strcpy(mempcpy(absPath, instance.state.platform.exePath.chars, pathLen + 1), path);
|
||||
return dlopen(absPath, FF_DLOPEN_FLAGS);
|
||||
|
||||
@@ -92,3 +99,81 @@ void* ffLibraryLoad(const char* path, int maxVersion, ...)
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
#if _WIN32
|
||||
|
||||
void* dlopen(const char* path, FF_MAYBE_UNUSED int mode)
|
||||
{
|
||||
wchar_t pathW[MAX_PATH + 1];
|
||||
ULONG pathWBytes = 0;
|
||||
|
||||
NTSTATUS status = RtlUTF8ToUnicodeN(pathW, sizeof(pathW), &pathWBytes, path, (uint32_t)strlen(path) + 1);
|
||||
if (!NT_SUCCESS(status))
|
||||
{
|
||||
FF_DEBUG("RtlUTF8ToUnicodeN failed for path %s with status 0x%08lX: %s", path, status, ffDebugNtStatus(status));
|
||||
return NULL;
|
||||
}
|
||||
|
||||
PVOID module = NULL;
|
||||
status = LdrLoadDll(NULL, NULL, &(UNICODE_STRING) {
|
||||
.Length = (USHORT) pathWBytes - sizeof(wchar_t), // Exclude null terminator
|
||||
.MaximumLength = (USHORT) pathWBytes,
|
||||
.Buffer = pathW,
|
||||
}, &module);
|
||||
|
||||
if (!NT_SUCCESS(status))
|
||||
{
|
||||
FF_DEBUG("LdrLoadDll failed for path %s with status 0x%08lX: %s", path, status, ffDebugNtStatus(status));
|
||||
return NULL;
|
||||
}
|
||||
|
||||
return module;
|
||||
}
|
||||
|
||||
int dlclose(void* handle)
|
||||
{
|
||||
NTSTATUS status = LdrUnloadDll(handle);
|
||||
if (!NT_SUCCESS(status))
|
||||
{
|
||||
FF_DEBUG("LdrUnloadDll failed for handle %p with status 0x%08lX: %s", handle, status, ffDebugNtStatus(status));
|
||||
return -1;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
void* dlsym(void* handle, const char* symbol)
|
||||
{
|
||||
void* address;
|
||||
USHORT symbolBytes = (USHORT) strlen(symbol) + 1;
|
||||
NTSTATUS status = LdrGetProcedureAddress(handle, &(ANSI_STRING) {
|
||||
.Length = symbolBytes - sizeof(char),
|
||||
.MaximumLength = symbolBytes,
|
||||
.Buffer = (char*) symbol,
|
||||
}, 0, &address);
|
||||
if (!NT_SUCCESS(status))
|
||||
{
|
||||
FF_DEBUG("LdrGetProcedureAddress failed for symbol %s with status 0x%08lX: %s", symbol, status, ffDebugNtStatus(status));
|
||||
return NULL;
|
||||
}
|
||||
return address;
|
||||
}
|
||||
|
||||
void* ffLibraryGetModule(const wchar_t* libraryFileName)
|
||||
{
|
||||
assert(libraryFileName != NULL && "Use \"ffGetPeb()->ImageBaseAddress\" instead");
|
||||
|
||||
void* module = NULL;
|
||||
USHORT libraryFileNameBytes = (USHORT) (wcslen(libraryFileName) * sizeof(wchar_t)) + sizeof(wchar_t);
|
||||
NTSTATUS status = LdrGetDllHandle(NULL, NULL, &(UNICODE_STRING) {
|
||||
.Length = libraryFileNameBytes - sizeof(wchar_t),
|
||||
.MaximumLength = libraryFileNameBytes,
|
||||
.Buffer = (wchar_t*) libraryFileName,
|
||||
}, &module);
|
||||
if (!NT_SUCCESS(status))
|
||||
{
|
||||
FF_DEBUG("LdrGetDllHandle failed for library %ls with status 0x%08lX: %s", libraryFileName, status, ffDebugNtStatus(status));
|
||||
return NULL;
|
||||
}
|
||||
return module;
|
||||
}
|
||||
#endif
|
||||
|
||||
@@ -108,8 +108,7 @@ static const char* tryNonThreadingFastPath(FFNetworkingState* state)
|
||||
#else
|
||||
"sendto()"
|
||||
#endif
|
||||
" %s (sent=%zd, errno=%d: %s)", errno == 0 ? "succeeded" : "was in progress",
|
||||
sent, errno, strerror(errno));
|
||||
" %s (sent=%zd, %s)", errno == 0 ? "succeeded" : "was in progress", sent, strerror(errno));
|
||||
freeaddrinfo(state->addr);
|
||||
state->addr = NULL;
|
||||
ffStrbufDestroy(&state->command);
|
||||
@@ -122,7 +121,7 @@ static const char* tryNonThreadingFastPath(FFNetworkingState* state)
|
||||
#else
|
||||
"sendto()"
|
||||
#endif
|
||||
" failed: %s (errno=%d)", strerror(errno), errno);
|
||||
" failed: %s", strerror(errno));
|
||||
#ifdef __APPLE__
|
||||
return "connectx() failed";
|
||||
#else
|
||||
@@ -143,7 +142,7 @@ static const char* connectAndSend(FFNetworkingState* state)
|
||||
FF_DEBUG("Attempting connect() to server...");
|
||||
if(connect(state->sockfd, state->addr->ai_addr, state->addr->ai_addrlen) == -1)
|
||||
{
|
||||
FF_DEBUG("connect() failed: %s (errno=%d)", strerror(errno), errno);
|
||||
FF_DEBUG("connect() failed: %s", strerror(errno));
|
||||
ret = "connect() failed";
|
||||
goto error;
|
||||
}
|
||||
@@ -152,7 +151,7 @@ static const char* connectAndSend(FFNetworkingState* state)
|
||||
FF_DEBUG("Attempting to send %u bytes of data...", state->command.length);
|
||||
if(send(state->sockfd, state->command.chars, state->command.length, 0) < 0)
|
||||
{
|
||||
FF_DEBUG("send() failed: %s (errno=%d)", strerror(errno), errno);
|
||||
FF_DEBUG("send() failed: %s", strerror(errno));
|
||||
ret = "send() failed";
|
||||
goto error;
|
||||
}
|
||||
@@ -227,7 +226,7 @@ static const char* initNetworkingState(FFNetworkingState* state, const char* hos
|
||||
state->sockfd = socket(state->addr->ai_family, state->addr->ai_socktype, state->addr->ai_protocol);
|
||||
if(state->sockfd == -1)
|
||||
{
|
||||
FF_DEBUG("socket() failed: %s (errno=%d)", strerror(errno), errno);
|
||||
FF_DEBUG("socket() failed: %s", strerror(errno));
|
||||
ret = "socket() failed";
|
||||
goto error;
|
||||
}
|
||||
@@ -399,7 +398,7 @@ const char* ffNetworkingRecvHttpResponse(FFNetworkingState* state, FFstrbuf* buf
|
||||
}
|
||||
else if (pollRes == -1)
|
||||
{
|
||||
FF_DEBUG("poll() failed: %s (errno=%d)", strerror(errno), errno);
|
||||
FF_DEBUG("poll() failed: %s", strerror(errno));
|
||||
close(state->sockfd);
|
||||
state->sockfd = -1;
|
||||
return "poll() failed";
|
||||
@@ -417,6 +416,12 @@ const char* ffNetworkingRecvHttpResponse(FFNetworkingState* state, FFstrbuf* buf
|
||||
}
|
||||
#endif
|
||||
|
||||
if (shutdown(state->sockfd, SHUT_WR) == -1)
|
||||
{
|
||||
FF_DEBUG("Failed to shutdown socket send: %s", strerror(errno));
|
||||
// Not a critical error, continue anyway
|
||||
}
|
||||
|
||||
FF_DEBUG("Starting data reception");
|
||||
FF_MAYBE_UNUSED int recvCount = 0;
|
||||
uint32_t contentLength = 0;
|
||||
@@ -434,7 +439,7 @@ const char* ffNetworkingRecvHttpResponse(FFNetworkingState* state, FFstrbuf* buf
|
||||
if (received == 0) {
|
||||
FF_DEBUG("Connection closed (received=0)");
|
||||
} else {
|
||||
FF_DEBUG("Reception failed: %s (errno=%d)", strerror(errno), errno);
|
||||
FF_DEBUG("Reception failed: %s", strerror(errno));
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
@@ -24,19 +24,19 @@ static const char* initWsaData(WSADATA* wsaData)
|
||||
}
|
||||
|
||||
//Dummy socket needed for WSAIoctl
|
||||
SOCKET sockfd = socket(AF_INET, SOCK_STREAM, 0);
|
||||
SOCKET sockfd = WSASocketW(AF_INET, SOCK_STREAM, 0, NULL, 0, 0);
|
||||
if(sockfd == INVALID_SOCKET) {
|
||||
FF_DEBUG("socket(AF_INET, SOCK_STREAM) failed");
|
||||
FF_DEBUG("WSASocketW(AF_INET, SOCK_STREAM) failed");
|
||||
WSACleanup();
|
||||
return "socket(AF_INET, SOCK_STREAM) failed";
|
||||
return "WSASocketW(AF_INET, SOCK_STREAM) failed";
|
||||
}
|
||||
|
||||
DWORD dwBytes;
|
||||
GUID guid = WSAID_CONNECTEX;
|
||||
if(WSAIoctl(sockfd, SIO_GET_EXTENSION_FUNCTION_POINTER,
|
||||
&guid, sizeof(guid),
|
||||
&ConnectEx, sizeof(ConnectEx),
|
||||
&dwBytes, NULL, NULL) != 0) {
|
||||
&guid, sizeof(guid),
|
||||
&ConnectEx, sizeof(ConnectEx),
|
||||
&dwBytes, NULL, NULL) != 0) {
|
||||
FF_DEBUG("WSAIoctl(sockfd, SIO_GET_EXTENSION_FUNCTION_POINTER) failed");
|
||||
closesocket(sockfd);
|
||||
WSACleanup();
|
||||
@@ -92,26 +92,33 @@ const char* ffNetworkingSendHttpRequest(FFNetworkingState* state, const char* ho
|
||||
return "initWsaData() failed before";
|
||||
}
|
||||
|
||||
struct addrinfo* addr;
|
||||
struct addrinfo hints = {
|
||||
ADDRINFOW* addr;
|
||||
ADDRINFOW hints = {
|
||||
.ai_flags = AI_NUMERICSERV,
|
||||
.ai_family = state->ipv6 ? AF_INET6 : AF_INET,
|
||||
.ai_socktype = SOCK_STREAM,
|
||||
.ai_flags = AI_NUMERICSERV,
|
||||
};
|
||||
|
||||
FF_DEBUG("Resolving address: %s (%s)", host, state->ipv6 ? "IPv6" : "IPv4");
|
||||
if(getaddrinfo(host, "80", &hints, &addr) != 0)
|
||||
wchar_t hostW[256];
|
||||
if (!NT_SUCCESS(RtlUTF8ToUnicodeN(hostW, (ULONG) sizeof(hostW), NULL, host, (ULONG) strlen(host) + 1)))
|
||||
{
|
||||
FF_DEBUG("getaddrinfo() failed");
|
||||
return "getaddrinfo() failed";
|
||||
FF_DEBUG("Failed to convert host to wide string: %s", host);
|
||||
return "Failed to convert host to wide string";
|
||||
}
|
||||
|
||||
state->sockfd = socket(addr->ai_family, addr->ai_socktype, addr->ai_protocol);
|
||||
FF_DEBUG("Resolving address: %s (%s)", host, state->ipv6 ? "IPv6" : "IPv4");
|
||||
if(GetAddrInfoW(hostW, L"80", &hints, &addr) != 0)
|
||||
{
|
||||
FF_DEBUG("GetAddrInfoW() failed");
|
||||
return "GetAddrInfoW() failed";
|
||||
}
|
||||
|
||||
state->sockfd = WSASocketW(addr->ai_family, addr->ai_socktype, addr->ai_protocol, NULL, 0, 0);
|
||||
if(state->sockfd == INVALID_SOCKET)
|
||||
{
|
||||
FF_DEBUG("socket() failed");
|
||||
freeaddrinfo(addr);
|
||||
return "socket() failed";
|
||||
FF_DEBUG("WSASocketW() failed");
|
||||
FreeAddrInfoW(addr);
|
||||
return "WSASocketW() failed";
|
||||
}
|
||||
|
||||
DWORD flag = 1;
|
||||
@@ -143,7 +150,7 @@ const char* ffNetworkingSendHttpRequest(FFNetworkingState* state, const char* ho
|
||||
{
|
||||
FF_DEBUG("bind() failed: %s", ffDebugWin32Error((DWORD) WSAGetLastError()));
|
||||
closesocket(state->sockfd);
|
||||
freeaddrinfo(addr);
|
||||
FreeAddrInfoW(addr);
|
||||
state->sockfd = INVALID_SOCKET;
|
||||
return "bind() failed";
|
||||
}
|
||||
@@ -156,7 +163,7 @@ const char* ffNetworkingSendHttpRequest(FFNetworkingState* state, const char* ho
|
||||
if (state->overlapped.hEvent == WSA_INVALID_EVENT) {
|
||||
FF_DEBUG("WSACreateEvent() failed");
|
||||
closesocket(state->sockfd);
|
||||
freeaddrinfo(addr);
|
||||
FreeAddrInfoW(addr);
|
||||
state->sockfd = INVALID_SOCKET;
|
||||
return "WSACreateEvent() failed";
|
||||
}
|
||||
@@ -200,7 +207,7 @@ const char* ffNetworkingSendHttpRequest(FFNetworkingState* state, const char* ho
|
||||
BOOL result = ConnectEx(state->sockfd, addr->ai_addr, (int)addr->ai_addrlen,
|
||||
state->command.chars, state->command.length, &sent, &state->overlapped);
|
||||
|
||||
freeaddrinfo(addr);
|
||||
FreeAddrInfoW(addr);
|
||||
addr = NULL;
|
||||
|
||||
if(!result)
|
||||
@@ -251,7 +258,8 @@ const char* ffNetworkingRecvHttpResponse(FFNetworkingState* state, FFstrbuf* buf
|
||||
} else {
|
||||
FF_DEBUG("WSAWaitForMultipleEvents failed: %s", ffDebugWin32Error((DWORD) WSAGetLastError()));
|
||||
}
|
||||
CancelIo((HANDLE) state->sockfd);
|
||||
if (CancelIoEx((HANDLE) state->sockfd, &state->overlapped))
|
||||
WSAWaitForMultipleEvents(1, &state->overlapped.hEvent, TRUE, 10, TRUE);
|
||||
WSACloseEvent(state->overlapped.hEvent);
|
||||
closesocket(state->sockfd);
|
||||
ffStrbufDestroy(&state->command);
|
||||
@@ -279,6 +287,12 @@ const char* ffNetworkingRecvHttpResponse(FFNetworkingState* state, FFstrbuf* buf
|
||||
// Not a critical error, continue anyway
|
||||
}
|
||||
|
||||
if (shutdown(state->sockfd, SD_SEND) == SOCKET_ERROR)
|
||||
{
|
||||
FF_DEBUG("Failed to shutdown socket send: %s", ffDebugWin32Error((DWORD) WSAGetLastError()));
|
||||
// Not a critical error, continue anyway
|
||||
}
|
||||
|
||||
if(timeout > 0)
|
||||
{
|
||||
FF_DEBUG("Setting receive timeout: %u ms", timeout);
|
||||
@@ -302,10 +316,14 @@ const char* ffNetworkingRecvHttpResponse(FFNetworkingState* state, FFstrbuf* buf
|
||||
FF_DEBUG("Data reception loop #%d, current buffer size: %u, available space: %u",
|
||||
++recvCount, buffer->length, ffStrbufGetFree(buffer));
|
||||
|
||||
ssize_t received = recv(state->sockfd, buffer->chars + buffer->length, (int)ffStrbufGetFree(buffer), 0);
|
||||
DWORD received = 0, recvFlags = 0;
|
||||
int recvResult = WSARecv(state->sockfd, &(WSABUF) {
|
||||
.buf = buffer->chars + buffer->length,
|
||||
.len = (ULONG) ffStrbufGetFree(buffer),
|
||||
}, 1, &received, &recvFlags, NULL, NULL);
|
||||
|
||||
if (received <= 0) {
|
||||
if (received == 0) {
|
||||
if (recvResult == SOCKET_ERROR || received == 0) {
|
||||
if (recvResult == 0 && received == 0) {
|
||||
FF_DEBUG("Connection closed (received=0)");
|
||||
} else {
|
||||
FF_DEBUG("Reception failed: %s", ffDebugWin32Error((DWORD) WSAGetLastError()));
|
||||
@@ -316,7 +334,7 @@ const char* ffNetworkingRecvHttpResponse(FFNetworkingState* state, FFstrbuf* buf
|
||||
buffer->length += (uint32_t) received;
|
||||
buffer->chars[buffer->length] = '\0';
|
||||
|
||||
FF_DEBUG("Successfully received %zd bytes of data, total: %u bytes", received, buffer->length);
|
||||
FF_DEBUG("Successfully received %u bytes of data, total: %u bytes", (unsigned) received, buffer->length);
|
||||
|
||||
// Check if HTTP header end marker is found
|
||||
if (headerEnd == 0) {
|
||||
|
||||
+260
-1
@@ -1,6 +1,6 @@
|
||||
#include "common/path.h"
|
||||
#include "common/io.h"
|
||||
#include "common/stringUtils.h"
|
||||
#include "common/arrayUtils.h"
|
||||
|
||||
#if !_WIN32
|
||||
const char* ffFindExecutableInPath(const char* name, FFstrbuf* result)
|
||||
@@ -49,6 +49,9 @@ const char* ffFindExecutableInPath(const char* name, FFstrbuf* result)
|
||||
}
|
||||
#else
|
||||
#include <windows.h>
|
||||
#include <winioctl.h>
|
||||
#include <errno.h>
|
||||
#include <stdalign.h>
|
||||
|
||||
const char* ffFindExecutableInPath(const char* name, FFstrbuf* result)
|
||||
{
|
||||
@@ -62,4 +65,260 @@ const char* ffFindExecutableInPath(const char* name, FFstrbuf* result)
|
||||
ffStrbufSetS(result, buffer);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
static inline int winerr2Errno(DWORD err)
|
||||
{
|
||||
switch (err)
|
||||
{
|
||||
case ERROR_FILE_NOT_FOUND:
|
||||
case ERROR_PATH_NOT_FOUND:
|
||||
case ERROR_INVALID_NAME:
|
||||
return ENOENT;
|
||||
case ERROR_ACCESS_DENIED:
|
||||
case ERROR_SHARING_VIOLATION:
|
||||
case ERROR_LOCK_VIOLATION:
|
||||
return EACCES;
|
||||
case ERROR_BUFFER_OVERFLOW:
|
||||
case ERROR_INSUFFICIENT_BUFFER:
|
||||
return ENAMETOOLONG;
|
||||
case ERROR_INVALID_PARAMETER:
|
||||
case ERROR_NOT_A_REPARSE_POINT:
|
||||
return EINVAL;
|
||||
default:
|
||||
return EIO;
|
||||
}
|
||||
}
|
||||
|
||||
char* frealpath(HANDLE hFile, char* resolved_name)
|
||||
{
|
||||
if (__builtin_expect(hFile == INVALID_HANDLE_VALUE || !hFile, false))
|
||||
{
|
||||
errno = EINVAL;
|
||||
return NULL;
|
||||
}
|
||||
|
||||
wchar_t resolvedNameW[MAX_PATH + 4]; /* +4 for "\\\\?\\" prefix */
|
||||
DWORD lenW = GetFinalPathNameByHandleW(hFile, resolvedNameW, (DWORD)ARRAY_SIZE(resolvedNameW), FILE_NAME_NORMALIZED);
|
||||
|
||||
if (lenW == 0)
|
||||
{
|
||||
errno = winerr2Errno(GetLastError());
|
||||
return NULL;
|
||||
}
|
||||
if (lenW >= ARRAY_SIZE(resolvedNameW))
|
||||
{
|
||||
errno = E2BIG;
|
||||
return NULL;
|
||||
}
|
||||
lenW++; // Include null terminator
|
||||
|
||||
wchar_t* srcW = resolvedNameW;
|
||||
DWORD srcLenW = lenW;
|
||||
|
||||
if (srcLenW >= 8 && wcsncmp(resolvedNameW, L"\\\\?\\UNC\\", 8) == 0)
|
||||
{
|
||||
/* Convert "\\?\UNC\server\share" to "\\server\share" */
|
||||
srcW += 6;
|
||||
srcLenW -= 6;
|
||||
*srcW = L'\\';
|
||||
}
|
||||
else if (srcLenW >= 4 && wcsncmp(resolvedNameW, L"\\\\?\\", 4) == 0)
|
||||
{
|
||||
srcW += 4;
|
||||
srcLenW -= 4;
|
||||
}
|
||||
|
||||
if (resolved_name)
|
||||
{
|
||||
ULONG outBytes = 0;
|
||||
if (!NT_SUCCESS(RtlUnicodeToUTF8N(resolved_name, MAX_PATH, &outBytes, srcW, (ULONG)(srcLenW * sizeof(wchar_t)))))
|
||||
{
|
||||
errno = E2BIG;
|
||||
return NULL;
|
||||
}
|
||||
|
||||
if (outBytes > MAX_PATH)
|
||||
{
|
||||
errno = E2BIG;
|
||||
return NULL;
|
||||
}
|
||||
|
||||
return resolved_name;
|
||||
}
|
||||
else
|
||||
{
|
||||
/* UTF-8 worst-case: up to 4 bytes per UTF-16 code unit */
|
||||
char tmp[(MAX_PATH + 4) * 4];
|
||||
ULONG outBytes = 0;
|
||||
|
||||
if (!NT_SUCCESS(RtlUnicodeToUTF8N(tmp, (ULONG)sizeof(tmp), &outBytes, srcW, (ULONG)(srcLenW * sizeof(wchar_t)))))
|
||||
{
|
||||
errno = E2BIG;
|
||||
return NULL;
|
||||
}
|
||||
|
||||
resolved_name = (char*)malloc(outBytes);
|
||||
if (!resolved_name)
|
||||
{
|
||||
errno = ENOMEM;
|
||||
return NULL;
|
||||
}
|
||||
|
||||
memcpy(resolved_name, tmp, outBytes);
|
||||
return resolved_name;
|
||||
}
|
||||
|
||||
return resolved_name;
|
||||
}
|
||||
|
||||
char* realpath(const char* __restrict file_name, char* __restrict resolved_name)
|
||||
{
|
||||
if (!file_name)
|
||||
{
|
||||
errno = EINVAL;
|
||||
return NULL;
|
||||
}
|
||||
|
||||
wchar_t fileNameW[MAX_PATH];
|
||||
ULONG lenBytes = 0;
|
||||
|
||||
if (!NT_SUCCESS(RtlUTF8ToUnicodeN(fileNameW, (ULONG)sizeof(fileNameW), &lenBytes, file_name, (ULONG)strlen(file_name) + 1)))
|
||||
{
|
||||
errno = EINVAL;
|
||||
return NULL;
|
||||
}
|
||||
|
||||
FF_AUTO_CLOSE_FD HANDLE hFile = CreateFileW(
|
||||
fileNameW,
|
||||
0,
|
||||
FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE,
|
||||
NULL,
|
||||
OPEN_EXISTING,
|
||||
FILE_FLAG_BACKUP_SEMANTICS,
|
||||
NULL);
|
||||
|
||||
if (hFile == INVALID_HANDLE_VALUE)
|
||||
{
|
||||
errno = winerr2Errno(GetLastError());
|
||||
return NULL;
|
||||
}
|
||||
|
||||
return frealpath(hFile, resolved_name);
|
||||
}
|
||||
|
||||
ssize_t freadlink(HANDLE hFile, char* buf, size_t bufsiz)
|
||||
{
|
||||
if (__builtin_expect(hFile == INVALID_HANDLE_VALUE || !buf || bufsiz == 0, false))
|
||||
{
|
||||
errno = EINVAL;
|
||||
return -1;
|
||||
}
|
||||
|
||||
alignas(REPARSE_DATA_BUFFER) BYTE reparseBuf[MAXIMUM_REPARSE_DATA_BUFFER_SIZE];
|
||||
DWORD bytesReturned = 0;
|
||||
if (!DeviceIoControl(hFile, FSCTL_GET_REPARSE_POINT, NULL, 0, reparseBuf, (DWORD) sizeof(reparseBuf), &bytesReturned, NULL))
|
||||
{
|
||||
errno = winerr2Errno(GetLastError());
|
||||
return -1;
|
||||
}
|
||||
|
||||
REPARSE_DATA_BUFFER* rp = (REPARSE_DATA_BUFFER*) reparseBuf;
|
||||
const wchar_t* targetW = NULL;
|
||||
USHORT targetBytes = 0;
|
||||
|
||||
if (rp->ReparseTag == IO_REPARSE_TAG_SYMLINK)
|
||||
{
|
||||
if (rp->SymbolicLinkReparseBuffer.PrintNameLength > 0)
|
||||
{
|
||||
targetW = rp->SymbolicLinkReparseBuffer.PathBuffer +
|
||||
(rp->SymbolicLinkReparseBuffer.PrintNameOffset / sizeof(wchar_t));
|
||||
targetBytes = rp->SymbolicLinkReparseBuffer.PrintNameLength;
|
||||
}
|
||||
else
|
||||
{
|
||||
targetW = rp->SymbolicLinkReparseBuffer.PathBuffer +
|
||||
(rp->SymbolicLinkReparseBuffer.SubstituteNameOffset / sizeof(wchar_t));
|
||||
targetBytes = rp->SymbolicLinkReparseBuffer.SubstituteNameLength;
|
||||
|
||||
if (targetBytes >= 8 &&
|
||||
wcsncmp(targetW, L"\\??\\", 4) == 0)
|
||||
{
|
||||
targetW += 4;
|
||||
targetBytes -= 8;
|
||||
}
|
||||
}
|
||||
}
|
||||
else if (rp->ReparseTag == IO_REPARSE_TAG_MOUNT_POINT)
|
||||
{
|
||||
if (rp->MountPointReparseBuffer.PrintNameLength > 0)
|
||||
{
|
||||
targetW = rp->MountPointReparseBuffer.PathBuffer +
|
||||
(rp->MountPointReparseBuffer.PrintNameOffset / sizeof(wchar_t));
|
||||
targetBytes = rp->MountPointReparseBuffer.PrintNameLength;
|
||||
}
|
||||
else
|
||||
{
|
||||
targetW = rp->MountPointReparseBuffer.PathBuffer +
|
||||
(rp->MountPointReparseBuffer.SubstituteNameOffset / sizeof(wchar_t));
|
||||
targetBytes = rp->MountPointReparseBuffer.SubstituteNameLength;
|
||||
|
||||
if (targetBytes >= 8 &&
|
||||
wcsncmp(targetW, L"\\??\\", 4) == 0)
|
||||
{
|
||||
targetW += 4;
|
||||
targetBytes -= 8;
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
errno = EINVAL;
|
||||
return -1;
|
||||
}
|
||||
|
||||
ULONG outBytes = 0;
|
||||
if (!NT_SUCCESS(RtlUnicodeToUTF8N(buf, (ULONG) bufsiz, &outBytes, targetW, targetBytes)))
|
||||
{
|
||||
errno = E2BIG;
|
||||
return -1;
|
||||
}
|
||||
|
||||
// Not null-terminated
|
||||
return (ssize_t) outBytes;
|
||||
}
|
||||
|
||||
ssize_t readlink(const char* path, char* buf, size_t bufsiz)
|
||||
{
|
||||
if (!path || !buf || bufsiz == 0)
|
||||
{
|
||||
errno = EINVAL;
|
||||
return -1;
|
||||
}
|
||||
|
||||
wchar_t pathW[MAX_PATH];
|
||||
ULONG pathWBytes = 0;
|
||||
if (!NT_SUCCESS(RtlUTF8ToUnicodeN(pathW, (ULONG) sizeof(pathW), &pathWBytes, path, (ULONG) strlen(path) + 1)))
|
||||
{
|
||||
errno = EINVAL;
|
||||
return -1;
|
||||
}
|
||||
|
||||
FF_AUTO_CLOSE_FD HANDLE hFile = CreateFileW(
|
||||
pathW,
|
||||
0,
|
||||
FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE,
|
||||
NULL,
|
||||
OPEN_EXISTING,
|
||||
FILE_FLAG_BACKUP_SEMANTICS | FILE_FLAG_OPEN_REPARSE_POINT,
|
||||
NULL
|
||||
);
|
||||
|
||||
if (hFile == INVALID_HANDLE_VALUE)
|
||||
{
|
||||
errno = winerr2Errno(GetLastError());
|
||||
return -1;
|
||||
}
|
||||
|
||||
return freadlink(hFile, buf, bufsiz);
|
||||
}
|
||||
#endif
|
||||
|
||||
@@ -47,8 +47,8 @@ void ffPrintLogoAndKey(const char* moduleName, uint8_t moduleIndex, const FFModu
|
||||
{
|
||||
FF_STRBUF_AUTO_DESTROY key = ffStrbufCreate();
|
||||
FF_PARSE_FORMAT_STRING_CHECKED(&key, &moduleArgs->key, ((FFformatarg[]) {
|
||||
FF_FORMAT_ARG(moduleIndex, "index"),
|
||||
FF_FORMAT_ARG(moduleArgs->keyIcon, "icon"),
|
||||
FF_ARG(moduleIndex, "index"),
|
||||
FF_ARG(moduleArgs->keyIcon, "icon"),
|
||||
}));
|
||||
ffStrbufWriteTo(&key, stdout);
|
||||
}
|
||||
|
||||
@@ -128,6 +128,8 @@ const char* ffProcessSpawn(char* const argv[], bool useStdErr, FFProcessHandle*
|
||||
{
|
||||
close(pipes[0]);
|
||||
close(pipes[1]);
|
||||
if (ret == ENOENT)
|
||||
return "command not found";
|
||||
return "posix_spawnp() failed";
|
||||
}
|
||||
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
#include "fastfetch.h"
|
||||
#include "common/mallocHelper.h"
|
||||
#include "common/processing.h"
|
||||
#include "common/io.h"
|
||||
#include "common/windows/unicode.h"
|
||||
@@ -85,7 +86,7 @@ const char* ffProcessSpawn(char* const argv[], bool useStdErr, FFProcessHandle*
|
||||
return "CreateFileW(L\"\\\\.\\pipe\\FASTFETCH-$(PID)\") failed";
|
||||
|
||||
PROCESS_INFORMATION piProcInfo = {};
|
||||
STARTUPINFOA siStartInfo = {
|
||||
STARTUPINFOW siStartInfo = {
|
||||
.cb = sizeof(siStartInfo),
|
||||
.dwFlags = STARTF_USESTDHANDLES,
|
||||
};
|
||||
@@ -100,12 +101,19 @@ const char* ffProcessSpawn(char* const argv[], bool useStdErr, FFProcessHandle*
|
||||
siStartInfo.hStdError = ffGetNullFD();
|
||||
}
|
||||
|
||||
FF_STRBUF_AUTO_DESTROY cmdline = ffStrbufCreate();
|
||||
argvToCmdline(argv, &cmdline);
|
||||
FF_AUTO_FREE wchar_t* cmdline = NULL;
|
||||
{
|
||||
FF_STRBUF_AUTO_DESTROY buf = ffStrbufCreate();
|
||||
argvToCmdline(argv, &buf);
|
||||
uint32_t cmdlineBytes = (buf.length + 1) * sizeof(wchar_t);
|
||||
cmdline = malloc(cmdlineBytes);
|
||||
if (!NT_SUCCESS(RtlUTF8ToUnicodeN(cmdline, cmdlineBytes, NULL, buf.chars, buf.length + 1)))
|
||||
return "RtlUTF8ToUnicodeN() failed";
|
||||
}
|
||||
|
||||
BOOL success = CreateProcessA(
|
||||
BOOL success = CreateProcessW(
|
||||
NULL, // application name
|
||||
cmdline.chars, // command line
|
||||
cmdline, // command line
|
||||
NULL, // process security attributes
|
||||
NULL, // primary thread security attributes
|
||||
TRUE, // handles are inherited
|
||||
@@ -116,15 +124,15 @@ const char* ffProcessSpawn(char* const argv[], bool useStdErr, FFProcessHandle*
|
||||
&piProcInfo // receives PROCESS_INFORMATION
|
||||
);
|
||||
|
||||
CloseHandle(hChildPipeWrite);
|
||||
NtClose(hChildPipeWrite);
|
||||
if(!success)
|
||||
{
|
||||
if (GetLastError() == ERROR_FILE_NOT_FOUND)
|
||||
return "command not found";
|
||||
return "CreateProcessA() failed";
|
||||
return "CreateProcessW() failed";
|
||||
}
|
||||
|
||||
CloseHandle(piProcInfo.hThread); // we don't need the thread handle
|
||||
NtClose(piProcInfo.hThread); // we don't need the thread handle
|
||||
outHandle->pid = piProcInfo.hProcess;
|
||||
outHandle->pipeRead = hChildPipeRead;
|
||||
hChildPipeRead = INVALID_HANDLE_VALUE; // ownership transferred, don't close it
|
||||
@@ -132,6 +140,17 @@ const char* ffProcessSpawn(char* const argv[], bool useStdErr, FFProcessHandle*
|
||||
return NULL;
|
||||
}
|
||||
|
||||
static void terminateChildProcess(HANDLE hProcess, HANDLE hChildPipeRead, HANDLE hReadEvent, IO_STATUS_BLOCK* piosb)
|
||||
{
|
||||
IO_STATUS_BLOCK cancelIosb = {};
|
||||
if (NT_SUCCESS(NtCancelIoFileEx(hChildPipeRead, piosb, &cancelIosb)))
|
||||
{
|
||||
if (hReadEvent)
|
||||
NtWaitForSingleObject(hReadEvent, FALSE, &(LARGE_INTEGER) { .QuadPart = -100000 }); // wait for cancellation to complete
|
||||
}
|
||||
NtTerminateProcess(hProcess, 1);
|
||||
}
|
||||
|
||||
const char* ffProcessReadOutput(FFProcessHandle* handle, FFstrbuf* buffer)
|
||||
{
|
||||
assert(handle->pipeRead != INVALID_HANDLE_VALUE);
|
||||
@@ -140,56 +159,59 @@ const char* ffProcessReadOutput(FFProcessHandle* handle, FFstrbuf* buffer)
|
||||
int32_t timeout = instance.config.general.processingTimeout;
|
||||
FF_AUTO_CLOSE_FD HANDLE hProcess = handle->pid;
|
||||
FF_AUTO_CLOSE_FD HANDLE hChildPipeRead = handle->pipeRead;
|
||||
FF_AUTO_CLOSE_FD HANDLE hReadEvent = NULL;
|
||||
handle->pid = INVALID_HANDLE_VALUE;
|
||||
handle->pipeRead = INVALID_HANDLE_VALUE;
|
||||
|
||||
if (timeout >= 0 && !NT_SUCCESS(NtCreateEvent(&hReadEvent, EVENT_ALL_ACCESS, NULL, SynchronizationEvent, FALSE)))
|
||||
return "NtCreateEvent() failed";
|
||||
|
||||
char str[FF_PIPE_BUFSIZ];
|
||||
DWORD nRead = 0;
|
||||
OVERLAPPED overlapped = {};
|
||||
// ReadFile always completes synchronously if the pipe is not created with FILE_FLAG_OVERLAPPED
|
||||
uint32_t nRead = 0;
|
||||
IO_STATUS_BLOCK iosb = {};
|
||||
do
|
||||
{
|
||||
if (!ReadFile(hChildPipeRead, str, sizeof(str), &nRead, &overlapped))
|
||||
NTSTATUS status = NtReadFile(
|
||||
hChildPipeRead,
|
||||
hReadEvent,
|
||||
NULL,
|
||||
NULL,
|
||||
&iosb,
|
||||
str,
|
||||
(ULONG) sizeof(str),
|
||||
NULL,
|
||||
NULL
|
||||
);
|
||||
if (status == STATUS_PENDING)
|
||||
{
|
||||
switch (GetLastError())
|
||||
switch (NtWaitForSingleObject(hReadEvent, FALSE, &(LARGE_INTEGER) { .QuadPart = (int64_t) timeout * -10000 }))
|
||||
{
|
||||
case ERROR_IO_PENDING:
|
||||
#if !FF_WIN7_COMPAT
|
||||
if (!GetOverlappedResultEx(hChildPipeRead, &overlapped, &nRead, timeout < 0 ? INFINITE : (DWORD) timeout, FALSE))
|
||||
#else
|
||||
// To support Windows 7
|
||||
if (timeout >= 0 && WaitForSingleObject(hChildPipeRead, (DWORD) timeout) != WAIT_OBJECT_0)
|
||||
{
|
||||
CancelIo(hChildPipeRead);
|
||||
TerminateProcess(hProcess, 1);
|
||||
return "WaitForSingleObject(hChildPipeRead) failed or timeout (try increasing --processing-timeout)";
|
||||
}
|
||||
|
||||
if (!GetOverlappedResult(hChildPipeRead, &overlapped, &nRead, FALSE))
|
||||
#endif
|
||||
{
|
||||
if (GetLastError() == ERROR_BROKEN_PIPE)
|
||||
return NULL;
|
||||
|
||||
CancelIo(hChildPipeRead);
|
||||
TerminateProcess(hProcess, 1);
|
||||
return "GetOverlappedResult"
|
||||
#if !FF_WIN7_COMPAT
|
||||
"Ex"
|
||||
#endif
|
||||
"(hChildPipeRead) failed";
|
||||
}
|
||||
case STATUS_WAIT_0:
|
||||
status = iosb.Status;
|
||||
break;
|
||||
|
||||
case ERROR_BROKEN_PIPE:
|
||||
goto exit;
|
||||
case STATUS_TIMEOUT:
|
||||
{
|
||||
terminateChildProcess(hProcess, hChildPipeRead, hReadEvent, &iosb);
|
||||
return "NtReadFile(hChildPipeRead) timed out";
|
||||
}
|
||||
|
||||
default:
|
||||
CancelIo(hChildPipeRead);
|
||||
TerminateProcess(hProcess, 1);
|
||||
return "ReadFile(hChildPipeRead) failed";
|
||||
terminateChildProcess(hProcess, hChildPipeRead, hReadEvent, &iosb);
|
||||
return "NtWaitForSingleObject(hReadEvent) failed";
|
||||
}
|
||||
}
|
||||
|
||||
if (status == STATUS_PIPE_BROKEN || status == STATUS_END_OF_FILE)
|
||||
goto exit;
|
||||
|
||||
if (!NT_SUCCESS(status))
|
||||
{
|
||||
terminateChildProcess(hProcess, hChildPipeRead, NULL, &iosb);
|
||||
return "NtReadFile(hChildPipeRead) failed";
|
||||
}
|
||||
|
||||
nRead = (uint32_t) iosb.Information;
|
||||
ffStrbufAppendNS(buffer, nRead, str);
|
||||
} while (nRead > 0);
|
||||
|
||||
@@ -212,12 +234,14 @@ exit:
|
||||
|
||||
bool ffProcessGetInfoWindows(uint32_t pid, uint32_t* ppid, FFstrbuf* pname, FFstrbuf* exe, const char** exeName, FFstrbuf* exePath, bool* gui)
|
||||
{
|
||||
FF_AUTO_CLOSE_FD HANDLE hProcess = pid == 0
|
||||
? NtCurrentProcess()
|
||||
: OpenProcess(PROCESS_QUERY_LIMITED_INFORMATION, FALSE, pid);
|
||||
|
||||
if (hProcess == NULL)
|
||||
return false;
|
||||
FF_AUTO_CLOSE_FD HANDLE hProcess = NtCurrentProcess();
|
||||
if(pid != 0)
|
||||
{
|
||||
if (!NT_SUCCESS(NtOpenProcess(&hProcess, PROCESS_QUERY_LIMITED_INFORMATION, &(OBJECT_ATTRIBUTES) {
|
||||
.Length = sizeof(OBJECT_ATTRIBUTES),
|
||||
}, &(CLIENT_ID) { .UniqueProcess = (HANDLE)(uintptr_t) pid })))
|
||||
return false;
|
||||
}
|
||||
|
||||
if(ppid)
|
||||
{
|
||||
|
||||
+173
-54
@@ -18,6 +18,7 @@ bool ffIsSmbiosValueSet(FFstrbuf* value)
|
||||
!ffStrbufIgnCaseEqualS(value, "None") &&
|
||||
!ffStrbufIgnCaseEqualS(value, "System Name") &&
|
||||
!ffStrbufIgnCaseEqualS(value, "System Version") &&
|
||||
!ffStrbufIgnCaseEqualS(value, "System SKU#") &&
|
||||
!ffStrbufIgnCaseEqualS(value, "Default string") &&
|
||||
!ffStrbufIgnCaseEqualS(value, "Undefined") &&
|
||||
!ffStrbufIgnCaseEqualS(value, "Not Specified") &&
|
||||
@@ -32,11 +33,29 @@ bool ffIsSmbiosValueSet(FFstrbuf* value)
|
||||
!ffStrbufIgnCaseEqualS(value, "All Series") &&
|
||||
!ffStrbufIgnCaseEqualS(value, "N/A") &&
|
||||
!ffStrbufIgnCaseEqualS(value, "Unknown") &&
|
||||
!ffStrbufIgnCaseEqualS(value, "Standard") &&
|
||||
!ffStrbufIgnCaseEqualS(value, "0x0000")
|
||||
!ffStrbufIgnCaseEqualS(value, "Standard") && ({
|
||||
// Some SMBIOS implementations use "0x0000" to indicate an unset value, even for strings.
|
||||
bool zero = ffStrbufStartsWithS(value, "0x0");
|
||||
if (zero)
|
||||
{
|
||||
for (size_t i = 2; i < value->length; i++)
|
||||
{
|
||||
char c = value->chars[i];
|
||||
if (c != '0')
|
||||
{
|
||||
zero = false;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
!zero;
|
||||
})
|
||||
;
|
||||
}
|
||||
|
||||
static bool smbiosTableInitialized = false;
|
||||
static FFSmbiosHeaderTable smbiosTable;
|
||||
|
||||
const FFSmbiosHeader* ffSmbiosNextEntry(const FFSmbiosHeader* header)
|
||||
{
|
||||
const char* p = ((const char*) header) + header->Length;
|
||||
@@ -52,6 +71,74 @@ const FFSmbiosHeader* ffSmbiosNextEntry(const FFSmbiosHeader* header)
|
||||
return (const FFSmbiosHeader*) (p + 1);
|
||||
}
|
||||
|
||||
static bool parseSmbiosTable(const uint8_t* data, uint32_t length)
|
||||
{
|
||||
const FFSmbiosHeader* endOfTable = NULL;
|
||||
|
||||
FF_DEBUG("Parsing SMBIOS table structures with length %u bytes", length);
|
||||
FF_MAYBE_UNUSED int structureCount = 0, totalCount = 0;
|
||||
for (
|
||||
const FFSmbiosHeader* header = (const FFSmbiosHeader*) data;
|
||||
(const uint8_t*) header + sizeof(FFSmbiosHeader) < (const uint8_t*) data + length;
|
||||
header = ffSmbiosNextEntry(header)
|
||||
)
|
||||
{
|
||||
++totalCount;
|
||||
endOfTable = header;
|
||||
|
||||
// This doesn't verify the entire structure (e.g. string section can still be truncated),
|
||||
// but at least ensures the formatted section is valid and prevents infinite loops
|
||||
// when the table is severely malformed.
|
||||
if (__builtin_expect((const uint8_t*) header + header->Length > (const uint8_t*) data + length, false))
|
||||
{
|
||||
FF_DEBUG("Truncated SMBIOS structure at offset 0x%lx: length %u is too small",
|
||||
(unsigned long)((const uint8_t*) header - data), header->Length);
|
||||
break;
|
||||
}
|
||||
|
||||
if (header->Type < FF_SMBIOS_TYPE_END_OF_TABLE)
|
||||
{
|
||||
if (!smbiosTable[header->Type])
|
||||
{
|
||||
smbiosTable[header->Type] = header;
|
||||
FF_DEBUG("Found SMBIOS structure type %u, handle 0x%04X, length %u",
|
||||
header->Type, header->Handle, header->Length);
|
||||
structureCount++;
|
||||
}
|
||||
else
|
||||
{
|
||||
FF_DEBUG("Duplicate SMBIOS structure type %u, handle 0x%04X, length %u",
|
||||
header->Type, header->Handle, header->Length);
|
||||
}
|
||||
}
|
||||
else if (header->Type == FF_SMBIOS_TYPE_END_OF_TABLE)
|
||||
{
|
||||
FF_DEBUG("Reached SMBIOS end of type %u, handle 0x%04X, length %u",
|
||||
header->Type, header->Handle, header->Length);
|
||||
break;
|
||||
}
|
||||
else
|
||||
{
|
||||
FF_DEBUG("Found custom SMBIOS structure type %u, handle 0x%04X, length %u; ignoring",
|
||||
header->Type, header->Handle, header->Length);
|
||||
}
|
||||
}
|
||||
|
||||
if (!endOfTable)
|
||||
{
|
||||
FF_DEBUG("No SMBIOS structures found in table");
|
||||
return false;
|
||||
}
|
||||
|
||||
FF_DEBUG("Parsed %d/%d SMBIOS structures, end-of-table (Type 127) %s",
|
||||
structureCount,
|
||||
totalCount,
|
||||
endOfTable->Type == FF_SMBIOS_TYPE_END_OF_TABLE ? "found." : "not found! SMBIOS data may be malformed.");
|
||||
smbiosTable[FF_SMBIOS_TYPE_END_OF_TABLE] = endOfTable;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
#if defined(__linux__) || defined(__FreeBSD__) || defined(__NetBSD__) || defined(__sun) || defined(__HAIKU__) || defined(__OpenBSD__) || defined(__GNU__)
|
||||
#include <fcntl.h>
|
||||
#include <sys/stat.h>
|
||||
@@ -71,17 +158,30 @@ const FFSmbiosHeader* ffSmbiosNextEntry(const FFSmbiosHeader* header)
|
||||
#define loff_t off_t
|
||||
#endif
|
||||
|
||||
#ifdef __linux__
|
||||
bool ffGetSmbiosValue(const char* devicesPath, const char* classPath, FFstrbuf* buffer)
|
||||
{
|
||||
if (ffReadFileBuffer(devicesPath, buffer))
|
||||
// /sys/class/dmi/id/* are all pseudo-files with very small content
|
||||
// so reading the whole file at once is efficient
|
||||
ffStrbufEnsureFixedLengthFree(buffer, 127);
|
||||
|
||||
ssize_t len = ffReadFileData(devicesPath, buffer->allocated - 1, buffer->chars);
|
||||
if (len > 0)
|
||||
{
|
||||
assert(len < buffer->allocated);
|
||||
buffer->chars[len] = '\0';
|
||||
buffer->length = (uint32_t) len;
|
||||
ffStrbufTrimRightSpace(buffer);
|
||||
if(ffIsSmbiosValueSet(buffer))
|
||||
return true;
|
||||
}
|
||||
|
||||
if (ffReadFileBuffer(classPath, buffer))
|
||||
len = ffReadFileData(classPath, buffer->allocated - 1, buffer->chars);
|
||||
if (len > 0)
|
||||
{
|
||||
assert(len < buffer->allocated);
|
||||
buffer->chars[len] = '\0';
|
||||
buffer->length = (uint32_t) len;
|
||||
ffStrbufTrimRightSpace(buffer);
|
||||
if(ffIsSmbiosValueSet(buffer))
|
||||
return true;
|
||||
@@ -90,6 +190,7 @@ bool ffGetSmbiosValue(const char* devicesPath, const char* classPath, FFstrbuf*
|
||||
ffStrbufClear(buffer);
|
||||
return false;
|
||||
}
|
||||
#endif
|
||||
|
||||
typedef struct FFSmbios20EntryPoint
|
||||
{
|
||||
@@ -137,10 +238,10 @@ typedef union FFSmbiosEntryPoint
|
||||
const FFSmbiosHeaderTable* ffGetSmbiosHeaderTable()
|
||||
{
|
||||
static FFstrbuf buffer;
|
||||
static FFSmbiosHeaderTable table;
|
||||
|
||||
if (buffer.chars == NULL)
|
||||
if (!smbiosTableInitialized)
|
||||
{
|
||||
smbiosTableInitialized = true;
|
||||
FF_DEBUG("Initializing SMBIOS buffer");
|
||||
ffStrbufInit(&buffer);
|
||||
#if !__HAIKU__ && !__OpenBSD__ && !__DragonFly__ && !__GNU__
|
||||
@@ -401,29 +502,8 @@ const FFSmbiosHeaderTable* ffGetSmbiosHeaderTable()
|
||||
}
|
||||
#endif
|
||||
|
||||
FF_DEBUG("Parsing SMBIOS table structures");
|
||||
FF_MAYBE_UNUSED int structureCount = 0;
|
||||
for (
|
||||
const FFSmbiosHeader* header = (const FFSmbiosHeader*) buffer.chars;
|
||||
(const uint8_t*) header < (const uint8_t*) buffer.chars + buffer.length;
|
||||
header = ffSmbiosNextEntry(header)
|
||||
)
|
||||
{
|
||||
if (header->Type < FF_SMBIOS_TYPE_END_OF_TABLE)
|
||||
{
|
||||
if (!table[header->Type]) {
|
||||
table[header->Type] = header;
|
||||
FF_DEBUG("Found SMBIOS structure type %u, handle 0x%04X, length %u",
|
||||
header->Type, header->Handle, header->Length);
|
||||
structureCount++;
|
||||
}
|
||||
}
|
||||
else if (header->Type == FF_SMBIOS_TYPE_END_OF_TABLE) {
|
||||
FF_DEBUG("Reached end-of-table marker");
|
||||
break;
|
||||
}
|
||||
}
|
||||
FF_DEBUG("Parsed %d SMBIOS structures", structureCount);
|
||||
if (!parseSmbiosTable((const uint8_t*) buffer.chars, buffer.length))
|
||||
ffStrbufClear(&buffer);
|
||||
}
|
||||
|
||||
if (buffer.length == 0) {
|
||||
@@ -431,7 +511,7 @@ const FFSmbiosHeaderTable* ffGetSmbiosHeaderTable()
|
||||
return NULL;
|
||||
}
|
||||
|
||||
return &table;
|
||||
return &smbiosTable;
|
||||
}
|
||||
#elif defined(_WIN32)
|
||||
#include "common/windows/nt.h"
|
||||
@@ -451,10 +531,10 @@ typedef struct FFRawSmbiosData
|
||||
const FFSmbiosHeaderTable* ffGetSmbiosHeaderTable()
|
||||
{
|
||||
static SYSTEM_FIRMWARE_TABLE_INFORMATION* buffer;
|
||||
static FFSmbiosHeaderTable table;
|
||||
|
||||
if (!buffer)
|
||||
if (!smbiosTableInitialized)
|
||||
{
|
||||
smbiosTableInitialized = true;
|
||||
FF_DEBUG("Initializing Windows SMBIOS buffer");
|
||||
|
||||
FF_DEBUG("Querying system firmware table size with signature 'RSMB'");
|
||||
@@ -491,31 +571,70 @@ const FFSmbiosHeaderTable* ffGetSmbiosHeaderTable()
|
||||
FF_DEBUG("Successfully retrieved SMBIOS data: version %u.%u, length %u bytes",
|
||||
rawData->SMBIOSMajorVersion, rawData->SMBIOSMinorVersion, rawData->Length);
|
||||
|
||||
FF_DEBUG("Parsing SMBIOS table structures");
|
||||
FF_MAYBE_UNUSED int structureCount = 0;
|
||||
for (
|
||||
const FFSmbiosHeader* header = (const FFSmbiosHeader*) rawData->SMBIOSTableData;
|
||||
(const uint8_t*) header < rawData->SMBIOSTableData + rawData->Length;
|
||||
header = ffSmbiosNextEntry(header)
|
||||
)
|
||||
if (!parseSmbiosTable(rawData->SMBIOSTableData, rawData->Length))
|
||||
{
|
||||
if (header->Type < FF_SMBIOS_TYPE_END_OF_TABLE)
|
||||
{
|
||||
if (!table[header->Type]) {
|
||||
table[header->Type] = header;
|
||||
FF_DEBUG("Found SMBIOS structure type %u, handle 0x%04X, length %u",
|
||||
header->Type, header->Handle, header->Length);
|
||||
structureCount++;
|
||||
}
|
||||
}
|
||||
else if (header->Type == FF_SMBIOS_TYPE_END_OF_TABLE) {
|
||||
FF_DEBUG("Reached end-of-table marker");
|
||||
break;
|
||||
}
|
||||
free(buffer);
|
||||
buffer = NULL;
|
||||
return NULL;
|
||||
}
|
||||
FF_DEBUG("Parsed %d SMBIOS structures", structureCount);
|
||||
}
|
||||
|
||||
return &table;
|
||||
if (!buffer) {
|
||||
FF_DEBUG("No valid SMBIOS data available");
|
||||
return NULL;
|
||||
}
|
||||
return &smbiosTable;
|
||||
}
|
||||
#elif defined(__APPLE__)
|
||||
#include "common/apple/cf_helpers.h"
|
||||
|
||||
const FFSmbiosHeaderTable* ffGetSmbiosHeaderTable()
|
||||
{
|
||||
static CFDataRef smbiosDataBuffer;
|
||||
|
||||
if (!smbiosTableInitialized)
|
||||
{
|
||||
smbiosTableInitialized = true;
|
||||
FF_DEBUG("Initializing SMBIOS buffer on Apple platform");
|
||||
|
||||
FF_IOOBJECT_AUTO_RELEASE io_registry_entry_t registryEntry = IOServiceGetMatchingService(MACH_PORT_NULL, IOServiceMatching("AppleSMBIOS"));
|
||||
|
||||
if (!registryEntry)
|
||||
{
|
||||
FF_DEBUG("IOServiceGetMatchingService() failed to find AppleSMBIOS");
|
||||
return NULL;
|
||||
}
|
||||
|
||||
FF_DEBUG("AppleSMBIOS service found, retrieving SMBIOS data");
|
||||
smbiosDataBuffer = IORegistryEntryCreateCFProperty(registryEntry, CFSTR("SMBIOS"), kCFAllocatorDefault, kNilOptions);
|
||||
if (!smbiosDataBuffer)
|
||||
{
|
||||
FF_DEBUG("IORegistryEntryCreateCFProperty() failed to get SMBIOS data");
|
||||
return NULL;
|
||||
}
|
||||
if (CFGetTypeID(smbiosDataBuffer) != CFDataGetTypeID())
|
||||
{
|
||||
FF_DEBUG("Unexpected SMBIOS data type: expected CFData");
|
||||
CFRelease(smbiosDataBuffer);
|
||||
smbiosDataBuffer = NULL;
|
||||
return NULL;
|
||||
}
|
||||
|
||||
FF_DEBUG("Successfully retrieved SMBIOS data: %lu bytes", CFDataGetLength(smbiosDataBuffer));
|
||||
if (!parseSmbiosTable((const uint8_t*) CFDataGetBytePtr(smbiosDataBuffer), (uint32_t) CFDataGetLength(smbiosDataBuffer)))
|
||||
{
|
||||
CFRelease(smbiosDataBuffer);
|
||||
smbiosDataBuffer = NULL;
|
||||
return NULL;
|
||||
}
|
||||
}
|
||||
|
||||
if (!smbiosDataBuffer)
|
||||
{
|
||||
FF_DEBUG("No valid SMBIOS data available");
|
||||
return NULL;
|
||||
}
|
||||
|
||||
return &smbiosTable;
|
||||
}
|
||||
#endif
|
||||
|
||||
@@ -89,7 +89,7 @@ FFTimeGetAgeResult ffTimeGetAge(uint64_t birthMs, uint64_t nowMs)
|
||||
static void ffTimeInitQpcMultiplier(void)
|
||||
{
|
||||
LARGE_INTEGER frequency;
|
||||
QueryPerformanceFrequency(&frequency);
|
||||
RtlQueryPerformanceFrequency(&frequency);
|
||||
ffQpcMultiplier = 1000. / (double) frequency.QuadPart;
|
||||
}
|
||||
#endif
|
||||
|
||||
+102
-42
@@ -7,6 +7,7 @@
|
||||
#include <fileapi.h>
|
||||
#include <handleapi.h>
|
||||
#include <io.h>
|
||||
#include "common/windows/nt.h"
|
||||
typedef HANDLE FFNativeFD;
|
||||
#define FF_INVALID_FD INVALID_HANDLE_VALUE
|
||||
#else
|
||||
@@ -15,6 +16,7 @@
|
||||
#include <sys/stat.h>
|
||||
#include <errno.h>
|
||||
#include <limits.h>
|
||||
#include <fcntl.h>
|
||||
typedef int FFNativeFD;
|
||||
#define FF_INVALID_FD (-1)
|
||||
// procfs's file can be changed between read calls such as /proc/meminfo and /proc/uptime.
|
||||
@@ -22,6 +24,51 @@
|
||||
#define PROC_FILE_BUFFSIZ (32 * 1024)
|
||||
#endif
|
||||
|
||||
#ifdef _WIN32
|
||||
#ifndef O_CLOEXEC
|
||||
#define O_CLOEXEC 0
|
||||
#endif
|
||||
#ifndef O_RDONLY
|
||||
#define O_RDONLY 0
|
||||
#endif
|
||||
#ifndef O_DIRECTORY
|
||||
#define O_DIRECTORY 0200000
|
||||
#endif
|
||||
|
||||
// Only O_RDONLY is supported
|
||||
HANDLE openat(HANDLE dfd, const char* fileName, int oflag);
|
||||
HANDLE openatW(HANDLE dfd, const wchar_t* fileName, uint16_t fileNameLen, bool directory);
|
||||
#endif
|
||||
|
||||
|
||||
static inline bool ffIsValidNativeFD(FFNativeFD fd)
|
||||
{
|
||||
#ifndef _WIN32
|
||||
return fd >= 0;
|
||||
#else
|
||||
// https://devblogs.microsoft.com/oldnewthing/20040302-00/?p=40443
|
||||
return fd != INVALID_HANDLE_VALUE && fd != NULL;
|
||||
#endif
|
||||
}
|
||||
|
||||
FF_C_NONNULL(1)
|
||||
static inline bool wrapClose(FFNativeFD* pfd)
|
||||
{
|
||||
assert(pfd);
|
||||
|
||||
if (!ffIsValidNativeFD(*pfd))
|
||||
return false;
|
||||
|
||||
#ifndef _WIN32
|
||||
close(*pfd);
|
||||
#else
|
||||
NtClose(*pfd);
|
||||
#endif
|
||||
|
||||
return true;
|
||||
}
|
||||
#define FF_AUTO_CLOSE_FD __attribute__((__cleanup__(wrapClose)))
|
||||
|
||||
static inline FFNativeFD FFUnixFD2NativeFD(int unixfd)
|
||||
{
|
||||
#ifndef _WIN32
|
||||
@@ -71,17 +118,60 @@ static inline ssize_t ffReadFDData(FFNativeFD fd, size_t dataSize, void* data)
|
||||
#endif
|
||||
}
|
||||
|
||||
FF_C_NONNULL(1, 3)
|
||||
ssize_t ffReadFileData(const char* fileName, size_t dataSize, void* data);
|
||||
FF_C_NONNULL(2, 4)
|
||||
ssize_t ffReadFileDataRelative(FFNativeFD dfd, const char* fileName, size_t dataSize, void* data);
|
||||
|
||||
FF_C_NONNULL(2)
|
||||
bool ffAppendFDBuffer(FFNativeFD fd, FFstrbuf* buffer);
|
||||
|
||||
FF_C_NONNULL(1, 3)
|
||||
static inline ssize_t ffReadFileData(const char* fileName, size_t dataSize, void* data)
|
||||
{
|
||||
FFNativeFD FF_AUTO_CLOSE_FD fd =
|
||||
#ifndef _WIN32
|
||||
open(fileName, O_RDONLY | O_CLOEXEC);
|
||||
#else
|
||||
CreateFileA(fileName, GENERIC_READ, FILE_SHARE_READ, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL);
|
||||
#endif
|
||||
|
||||
if (!ffIsValidNativeFD(fd))
|
||||
return -1;
|
||||
|
||||
return ffReadFDData(fd, dataSize, data);
|
||||
}
|
||||
|
||||
FF_C_NONNULL(2, 4)
|
||||
static inline ssize_t ffReadFileDataRelative(FFNativeFD dfd, const char* fileName, size_t dataSize, void* data)
|
||||
{
|
||||
FFNativeFD FF_AUTO_CLOSE_FD fd = openat(dfd, fileName, O_RDONLY | O_CLOEXEC);
|
||||
if (!ffIsValidNativeFD(fd))
|
||||
return -1;
|
||||
|
||||
return ffReadFDData(fd, dataSize, data);
|
||||
}
|
||||
|
||||
FF_C_NONNULL(1, 2)
|
||||
bool ffAppendFileBuffer(const char* fileName, FFstrbuf* buffer);
|
||||
static inline bool ffAppendFileBuffer(const char* fileName, FFstrbuf* buffer)
|
||||
{
|
||||
FFNativeFD FF_AUTO_CLOSE_FD fd =
|
||||
#ifndef _WIN32
|
||||
open(fileName, O_RDONLY | O_CLOEXEC);
|
||||
#else
|
||||
CreateFileA(fileName, GENERIC_READ, FILE_SHARE_READ, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL);
|
||||
#endif
|
||||
|
||||
if (!ffIsValidNativeFD(fd))
|
||||
return false;
|
||||
|
||||
return ffAppendFDBuffer(fd, buffer);
|
||||
}
|
||||
|
||||
FF_C_NONNULL(2, 3)
|
||||
bool ffAppendFileBufferRelative(FFNativeFD dfd, const char* fileName, FFstrbuf* buffer);
|
||||
static inline bool ffAppendFileBufferRelative(FFNativeFD dfd, const char* fileName, FFstrbuf* buffer)
|
||||
{
|
||||
FFNativeFD FF_AUTO_CLOSE_FD fd = openat(dfd, fileName, O_RDONLY | O_CLOEXEC);
|
||||
if (!ffIsValidNativeFD(fd))
|
||||
return false;
|
||||
|
||||
return ffAppendFDBuffer(fd, buffer);
|
||||
}
|
||||
|
||||
FF_C_NONNULL(2)
|
||||
static inline bool ffReadFDBuffer(FFNativeFD fd, FFstrbuf* buffer)
|
||||
@@ -117,7 +207,11 @@ static inline bool ffPathExists(const char* path, FFPathType pathType)
|
||||
{
|
||||
#ifdef _WIN32
|
||||
|
||||
DWORD attr = GetFileAttributesA(path);
|
||||
wchar_t wPath[MAX_PATH];
|
||||
if (!NT_SUCCESS(RtlUTF8ToUnicodeN(wPath, (ULONG) sizeof(wPath), NULL, path, (ULONG)strlen(path) + 1)))
|
||||
return false;
|
||||
|
||||
DWORD attr = GetFileAttributesW(wPath);
|
||||
|
||||
if(attr == INVALID_FILE_ATTRIBUTES)
|
||||
return false;
|
||||
@@ -178,34 +272,6 @@ static inline void ffUnsuppressIO(bool* suppressed)
|
||||
|
||||
void ffListFilesRecursively(const char* path, bool pretty);
|
||||
|
||||
static inline bool ffIsValidNativeFD(FFNativeFD fd)
|
||||
{
|
||||
#ifndef _WIN32
|
||||
return fd >= 0;
|
||||
#else
|
||||
// https://devblogs.microsoft.com/oldnewthing/20040302-00/?p=40443
|
||||
return fd != INVALID_HANDLE_VALUE && fd != NULL;
|
||||
#endif
|
||||
}
|
||||
|
||||
FF_C_NONNULL(1)
|
||||
static inline bool wrapClose(FFNativeFD* pfd)
|
||||
{
|
||||
assert(pfd);
|
||||
|
||||
if (!ffIsValidNativeFD(*pfd))
|
||||
return false;
|
||||
|
||||
#ifndef _WIN32
|
||||
close(*pfd);
|
||||
#else
|
||||
CloseHandle(*pfd);
|
||||
#endif
|
||||
|
||||
return true;
|
||||
}
|
||||
#define FF_AUTO_CLOSE_FD __attribute__((__cleanup__(wrapClose)))
|
||||
|
||||
FF_C_NONNULL(1)
|
||||
static inline bool wrapFclose(FILE** pfile)
|
||||
{
|
||||
@@ -257,9 +323,3 @@ static inline bool ffSearchUserConfigFile(const FFlist* configDirs, const char*
|
||||
|
||||
FFNativeFD ffGetNullFD(void);
|
||||
bool ffRemoveFile(const char* fileName);
|
||||
|
||||
#ifdef _WIN32
|
||||
// Only O_RDONLY is supported
|
||||
HANDLE openat(HANDLE dfd, const char* fileName, bool directory);
|
||||
HANDLE openatW(HANDLE dfd, const wchar_t* fileName, uint16_t fileNameLen, bool directory);
|
||||
#endif
|
||||
|
||||
+17
-15
@@ -6,11 +6,10 @@
|
||||
#ifndef FF_DISABLE_DLOPEN
|
||||
|
||||
#if defined(_WIN32)
|
||||
#include <libloaderapi.h>
|
||||
#define FF_DLOPEN_FLAGS 0
|
||||
FF_C_NODISCARD static inline void* dlopen(const char* path, int mode) { FF_UNUSED(mode); return LoadLibraryA(path); }
|
||||
FF_C_NODISCARD static inline void* dlsym(void* handle, const char* symbol) { return (void*) GetProcAddress((HMODULE)handle, symbol); }
|
||||
static inline int dlclose(void* handle) { return !FreeLibrary((HMODULE)handle); }
|
||||
FF_C_NODISCARD void* dlopen(const char* path, int mode);
|
||||
FF_C_NODISCARD void* dlsym(void* handle, const char* symbol);
|
||||
int dlclose(void* handle);
|
||||
#else
|
||||
#include <dlfcn.h>
|
||||
#endif
|
||||
@@ -30,6 +29,10 @@ static inline void ffLibraryUnload(void** handle)
|
||||
dlclose(*handle);
|
||||
}
|
||||
|
||||
#if __cplusplus
|
||||
#define __auto_type auto
|
||||
#endif
|
||||
|
||||
#define FF_LIBRARY_SYMBOL(symbolName) \
|
||||
__typeof__(&symbolName) ff ## symbolName;
|
||||
|
||||
@@ -38,13 +41,8 @@ static inline void ffLibraryUnload(void** handle)
|
||||
if(libraryObjectName == NULL) \
|
||||
return returnValue;
|
||||
|
||||
#if _WIN32
|
||||
#define FF_LIBRARY_LOAD_MESSAGE(libraryObjectName, libraryFileName, maxVersion, ...) \
|
||||
FF_LIBRARY_LOAD(libraryObjectName, "LoadLibraryA(" libraryFileName ") failed", libraryFileName, maxVersion, ##__VA_ARGS__)
|
||||
#else
|
||||
#define FF_LIBRARY_LOAD_MESSAGE(libraryObjectName, libraryFileName, maxVersion, ...) \
|
||||
FF_LIBRARY_LOAD(libraryObjectName, "dlopen(" libraryFileName ") failed", libraryFileName, maxVersion, ##__VA_ARGS__)
|
||||
#endif
|
||||
|
||||
#define FF_LIBRARY_LOAD_SYMBOL_ADDRESS(library, symbolMapping, symbolName, returnValue) \
|
||||
symbolMapping = (__typeof__(&symbolName)) dlsym(library, #symbolName); \
|
||||
@@ -52,13 +50,13 @@ static inline void ffLibraryUnload(void** handle)
|
||||
return returnValue;
|
||||
|
||||
#define FF_LIBRARY_LOAD_SYMBOL(library, symbolName, returnValue) \
|
||||
__typeof__(&symbolName) FF_LIBRARY_LOAD_SYMBOL_ADDRESS(library, ff ## symbolName, symbolName, returnValue);
|
||||
__auto_type FF_LIBRARY_LOAD_SYMBOL_ADDRESS(library, ff ## symbolName, symbolName, returnValue);
|
||||
|
||||
#define FF_LIBRARY_LOAD_SYMBOL_LAZY(library, symbolName) \
|
||||
__typeof__(&symbolName) ff ## symbolName = (__typeof__(&symbolName)) dlsym(library, #symbolName);
|
||||
__auto_type ff ## symbolName = (__typeof__(&symbolName)) dlsym(library, #symbolName);
|
||||
|
||||
#define FF_LIBRARY_LOAD_SYMBOL_MESSAGE(library, symbolName) \
|
||||
__typeof__(&symbolName) FF_LIBRARY_LOAD_SYMBOL_ADDRESS(library, ff ## symbolName, symbolName, "dlsym " #symbolName " failed");
|
||||
__auto_type FF_LIBRARY_LOAD_SYMBOL_ADDRESS(library, ff ## symbolName, symbolName, "dlsym " #symbolName " failed");
|
||||
|
||||
#define FF_LIBRARY_LOAD_SYMBOL_VAR(library, varName, symbolName, returnValue) \
|
||||
FF_LIBRARY_LOAD_SYMBOL_ADDRESS(library, (varName).ff ## symbolName, symbolName, returnValue);
|
||||
@@ -88,13 +86,13 @@ void* ffLibraryLoad(const char* path, int maxVersion, ...);
|
||||
symbolMapping = (__typeof__(&symbolName)) &symbolName;
|
||||
|
||||
#define FF_LIBRARY_LOAD_SYMBOL(library, symbolName, returnValue) \
|
||||
FF_MAYBE_UNUSED __typeof__(&symbolName) FF_LIBRARY_LOAD_SYMBOL_ADDRESS(library, ff ## symbolName, symbolName, returnValue);
|
||||
FF_MAYBE_UNUSED __auto_type FF_LIBRARY_LOAD_SYMBOL_ADDRESS(library, ff ## symbolName, symbolName, returnValue);
|
||||
|
||||
#define FF_LIBRARY_LOAD_SYMBOL_LAZY(library, symbolName) \
|
||||
FF_MAYBE_UNUSED __typeof__(&symbolName) ff ## symbolName = (__typeof__(&symbolName)) &symbolName;
|
||||
FF_MAYBE_UNUSED __auto_type ff ## symbolName = (__typeof__(&symbolName)) &symbolName;
|
||||
|
||||
#define FF_LIBRARY_LOAD_SYMBOL_MESSAGE(library, symbolName) \
|
||||
FF_MAYBE_UNUSED __typeof__(&symbolName) FF_LIBRARY_LOAD_SYMBOL_ADDRESS(library, ff ## symbolName, symbolName, "dlsym " #symbolName " failed");
|
||||
FF_MAYBE_UNUSED __auto_type FF_LIBRARY_LOAD_SYMBOL_ADDRESS(library, ff ## symbolName, symbolName, "dlsym " #symbolName " failed");
|
||||
|
||||
#define FF_LIBRARY_LOAD_SYMBOL_VAR(library, varName, symbolName, returnValue) \
|
||||
FF_LIBRARY_LOAD_SYMBOL_ADDRESS(library, (varName).ff ## symbolName, symbolName, returnValue);
|
||||
@@ -106,3 +104,7 @@ void* ffLibraryLoad(const char* path, int maxVersion, ...);
|
||||
FF_LIBRARY_LOAD_SYMBOL_ADDRESS(library, (varName)->ff ## symbolName, symbolName, returnValue);
|
||||
|
||||
#endif
|
||||
|
||||
#if _WIN32
|
||||
void* ffLibraryGetModule(const wchar_t* libraryFileName);
|
||||
#endif
|
||||
|
||||
@@ -13,3 +13,10 @@ static inline bool ffIsAbsolutePath(const char* path)
|
||||
return path[0] == '/';
|
||||
#endif
|
||||
}
|
||||
|
||||
#if _WIN32
|
||||
char* frealpath(void* __restrict hFile, char* __restrict resolved_name /*MAX_PATH*/);
|
||||
char* realpath(const char* __restrict file_name, char* __restrict resolved_name /*MAX_PATH*/);
|
||||
ssize_t freadlink(void* hFile, char* buf, size_t bufsiz);
|
||||
ssize_t readlink(const char* path, char* buf, size_t bufsiz);
|
||||
#endif
|
||||
|
||||
@@ -63,14 +63,22 @@ typedef enum __attribute__((__packed__)) FFSmbiosType // : uint8_t
|
||||
FF_SMBIOS_TYPE_STRING_PROPERTY = 46,
|
||||
FF_SMBIOS_TYPE_INACTIVE = 126,
|
||||
FF_SMBIOS_TYPE_END_OF_TABLE = 127,
|
||||
FF_SMBIOS_TYPE__MAX,
|
||||
// system- and OEM-specific information 128~256
|
||||
} FFSmbiosType;
|
||||
static_assert(sizeof(FFSmbiosType) == 1, "FFSmbiosType should be 1 byte");
|
||||
|
||||
typedef struct FFSmbiosHeader
|
||||
{
|
||||
// Type of SMBIOS structure. Do NOT test `Type == FF_SMBIOS_END_OF_TABLE` to determine the end of the table,
|
||||
// as malformed tables may be missing the end-of-table marker.
|
||||
// **Use FFSmbiosHeaderTable[FF_SMBIOS_TYPE_END_OF_TABLE] pointer instead.**
|
||||
FFSmbiosType Type;
|
||||
// Length of formatted section, excluding unformatted string section
|
||||
// Must be at least 4 (sizeof(FFSmbiosHeader)) to be valid
|
||||
uint8_t Length;
|
||||
// Unique handle, used to reference this structure from other structures.
|
||||
// Not guaranteed to be consistent across reboots or even multiple reads of the same table.
|
||||
uint16_t Handle;
|
||||
} __attribute__((__packed__)) FFSmbiosHeader;
|
||||
static_assert(sizeof(FFSmbiosHeader) == 4, "FFSmbiosHeader should be 4 bytes");
|
||||
@@ -84,7 +92,7 @@ static inline const char* ffSmbiosLocateString(const char* start, uint8_t index
|
||||
return start;
|
||||
}
|
||||
|
||||
typedef const FFSmbiosHeader* FFSmbiosHeaderTable[FF_SMBIOS_TYPE_END_OF_TABLE];
|
||||
typedef const FFSmbiosHeader* FFSmbiosHeaderTable[FF_SMBIOS_TYPE__MAX];
|
||||
|
||||
const FFSmbiosHeader* ffSmbiosNextEntry(const FFSmbiosHeader* header);
|
||||
const FFSmbiosHeaderTable* ffGetSmbiosHeaderTable();
|
||||
|
||||
@@ -80,6 +80,23 @@ static inline bool ffCharIsDigit(char c)
|
||||
return '0' <= c && c <= '9';
|
||||
}
|
||||
|
||||
static inline bool ffCharIsHexDigit(char c)
|
||||
{
|
||||
return ffCharIsDigit(c) || ('a' <= c && c <= 'f') || ('A' <= c && c <= 'F');
|
||||
}
|
||||
|
||||
static inline int8_t ffHexCharToInt(char c)
|
||||
{
|
||||
if (ffCharIsDigit(c))
|
||||
return (int8_t) (c - '0');
|
||||
else if ('a' <= c && c <= 'f')
|
||||
return (int8_t) (c - 'a' + 10);
|
||||
else if ('A' <= c && c <= 'F')
|
||||
return (int8_t) (c - 'A' + 10);
|
||||
else
|
||||
return -1;
|
||||
}
|
||||
|
||||
// Copies at most (dstBufSiz - 1) bytes from src to dst; dst is always null-terminated
|
||||
static inline char* ffStrCopy(char* __restrict__ dst, const char* __restrict__ src, size_t dstBufSiz)
|
||||
{
|
||||
|
||||
+5
-5
@@ -4,7 +4,7 @@
|
||||
|
||||
#ifdef FF_HAVE_THREADS
|
||||
#if defined(_WIN32)
|
||||
#include <handleapi.h>
|
||||
#include <winternl.h>
|
||||
#include <synchapi.h>
|
||||
#include <process.h>
|
||||
#include <processthreadsapi.h>
|
||||
@@ -18,16 +18,16 @@
|
||||
}
|
||||
#define FF_THREAD_ENTRY_DECL_WRAPPER(fn, paramType) static __stdcall unsigned fn ## ThreadMain (void* data) { fn((paramType)data); return 0; }
|
||||
#define FF_THREAD_ENTRY_DECL_WRAPPER_NOPARAM(fn) static __stdcall unsigned fn ## ThreadMain () { fn(); return 0; }
|
||||
static inline void ffThreadDetach(FFThreadType thread) { CloseHandle(thread); }
|
||||
static inline void ffThreadDetach(FFThreadType thread) { NtClose(thread); }
|
||||
static inline bool ffThreadJoin(FFThreadType thread, uint32_t timeout)
|
||||
{
|
||||
if (WaitForSingleObject(thread, timeout == 0 ? (DWORD) -1 : timeout) != 0 /*WAIT_OBJECT_0*/)
|
||||
if (NtWaitForSingleObject(thread, FALSE, timeout == 0 ? NULL : &(LARGE_INTEGER) { .QuadPart = (int64_t) timeout * -10000 }) != STATUS_WAIT_0)
|
||||
{
|
||||
TerminateThread(thread, (DWORD) -1);
|
||||
CloseHandle(thread);
|
||||
NtClose(thread);
|
||||
return false;
|
||||
}
|
||||
CloseHandle(thread);
|
||||
NtClose(thread);
|
||||
return true;
|
||||
}
|
||||
#else
|
||||
|
||||
+11
-2
@@ -18,7 +18,7 @@ static inline double ffTimeGetTick(void) //In msec
|
||||
#ifdef _WIN32
|
||||
extern double ffQpcMultiplier;
|
||||
LARGE_INTEGER start;
|
||||
QueryPerformanceCounter(&start);
|
||||
RtlQueryPerformanceCounter(&start);
|
||||
return (double) start.QuadPart * ffQpcMultiplier;
|
||||
#elif defined(__HAIKU__)
|
||||
return (double) system_time() / 1000.;
|
||||
@@ -29,11 +29,20 @@ static inline double ffTimeGetTick(void) //In msec
|
||||
#endif
|
||||
}
|
||||
|
||||
#if _WIN32
|
||||
static inline uint64_t ffFileTimeToUnixMs(uint64_t value)
|
||||
{
|
||||
if (__builtin_expect(__builtin_usubll_overflow(value, 116444736000000000ull, &value), false))
|
||||
return 0;
|
||||
return value / 10000ull;
|
||||
}
|
||||
#endif
|
||||
|
||||
static inline uint64_t ffTimeGetNow(void)
|
||||
{
|
||||
#ifdef _WIN32
|
||||
uint64_t timeNow = ffKSystemTimeToUInt64(&SharedUserData->SystemTime);
|
||||
return (timeNow - 116444736000000000ull) / 10000ull;
|
||||
return ffFileTimeToUnixMs((uint64_t) timeNow);
|
||||
#elif defined(__HAIKU__)
|
||||
return (uint64_t) real_time_clock_usecs() / 1000u;
|
||||
#else
|
||||
|
||||
@@ -17,7 +17,8 @@ static const char* doInitCom()
|
||||
return "CoInitializeEx() failed";
|
||||
|
||||
// Set general COM security levels
|
||||
if (FAILED(CoInitializeSecurity(
|
||||
|
||||
HRESULT hRes = CoInitializeSecurity(
|
||||
NULL,
|
||||
-1, // COM authentication
|
||||
NULL, // Authentication services
|
||||
@@ -27,7 +28,9 @@ static const char* doInitCom()
|
||||
NULL, // Authentication info
|
||||
EOAC_NONE, // Additional capabilities
|
||||
NULL // Reserved
|
||||
)))
|
||||
);
|
||||
|
||||
if (FAILED(hRes) && hRes != RPC_E_TOO_LATE /* Has been set by a random dll */)
|
||||
{
|
||||
CoUninitialize();
|
||||
return "CoInitializeSecurity() failed";
|
||||
|
||||
@@ -2,14 +2,8 @@
|
||||
<assembly xmlns="urn:schemas-microsoft-com:asm.v1" manifestVersion="1.0" xmlns:asmv3="urn:schemas-microsoft-com:asm.v3">
|
||||
<compatibility xmlns="urn:schemas-microsoft-com:compatibility.v1">
|
||||
<application>
|
||||
<!--This Id value indicates the application supports Windows 7 functionality-->
|
||||
<supportedOS Id="{35138b9a-5d96-4fbd-8e2d-a2440225f93a}"/>
|
||||
<!--This Id value indicates the application supports Windows 8 functionality-->
|
||||
<supportedOS Id="{4a2f28e3-53b9-4441-ba9c-d69d4a4a6e38}"/>
|
||||
<!--This Id value indicates the application supports Windows 8.1 functionality-->
|
||||
<supportedOS Id="{1f676c76-80e1-4239-95bb-83d0f6d0da78}"/>
|
||||
<!--This Id value indicates the application supports Windows 10, 11 functionality-->
|
||||
<supportedOS Id="{8e0f7a12-bfb3-4fe8-b9a5-48fd50a15a9a}"/>
|
||||
<supportedOS Id="{1f676c76-80e1-4239-95bb-83d0f6d0da78}"/><!-- 8.1 -->
|
||||
<supportedOS Id="{8e0f7a12-bfb3-4fe8-b9a5-48fd50a15a9a}"/><!-- 10/11 -->
|
||||
</application>
|
||||
</compatibility>
|
||||
<assemblyIdentity type="win32" name="com.github.fastfetch" version="0.0.0.0"/>
|
||||
|
||||
+933
-219
File diff suppressed because it is too large
Load Diff
+362
-111
@@ -1,17 +1,22 @@
|
||||
#include "registry.h"
|
||||
#include "unicode.h"
|
||||
#include "common/mallocHelper.h"
|
||||
#include "common/debug.h"
|
||||
#include "common/windows/nt.h"
|
||||
|
||||
static const char* hKey2Str(HKEY hKey)
|
||||
#include <stdalign.h>
|
||||
#include <ntstatus.h>
|
||||
|
||||
static HANDLE hRootKeys[8 /*(uintptr_t) HKEY_CURRENT_USER_LOCAL_SETTINGS - (uintptr_t) HKEY_CLASSES_ROOT + 1*/];
|
||||
|
||||
static const char* hKey2Str(HANDLE hRootKey)
|
||||
{
|
||||
#define HKEY_CASE(compareKey) if(hKey == compareKey) return #compareKey;
|
||||
#define HKEY_CASE(compareKey) if(hRootKey == hRootKeys[(uintptr_t)compareKey - (uintptr_t)HKEY_CLASSES_ROOT]) return #compareKey;
|
||||
HKEY_CASE(HKEY_CLASSES_ROOT)
|
||||
HKEY_CASE(HKEY_CURRENT_USER)
|
||||
HKEY_CASE(HKEY_LOCAL_MACHINE)
|
||||
HKEY_CASE(HKEY_USERS)
|
||||
HKEY_CASE(HKEY_PERFORMANCE_DATA)
|
||||
HKEY_CASE(HKEY_PERFORMANCE_TEXT)
|
||||
HKEY_CASE(HKEY_PERFORMANCE_NLSTEXT)
|
||||
HKEY_CASE(HKEY_CURRENT_CONFIG)
|
||||
HKEY_CASE(HKEY_DYN_DATA)
|
||||
HKEY_CASE(HKEY_CURRENT_USER_LOCAL_SETTINGS)
|
||||
@@ -20,152 +25,398 @@ static const char* hKey2Str(HKEY hKey)
|
||||
return "UNKNOWN";
|
||||
}
|
||||
|
||||
bool ffRegOpenKeyForRead(HKEY hKey, const wchar_t* subKeyW, HKEY* result, FFstrbuf* error)
|
||||
HANDLE ffRegGetRootKeyHandle(HKEY hKey)
|
||||
{
|
||||
if(RegOpenKeyExW(hKey, subKeyW, 0, KEY_READ, result) != ERROR_SUCCESS)
|
||||
assert(hKey);
|
||||
assert((uintptr_t) hKey >= (uintptr_t) HKEY_CLASSES_ROOT && (uintptr_t) hKey <= (uintptr_t) HKEY_CURRENT_USER_LOCAL_SETTINGS);
|
||||
|
||||
FF_DEBUG("Getting root key handle for HKEY %08llx", (uint64_t)(uintptr_t) hKey);
|
||||
|
||||
HANDLE result = hRootKeys[(uintptr_t) hKey - (uintptr_t) HKEY_CLASSES_ROOT];
|
||||
if (result)
|
||||
{
|
||||
if(error)
|
||||
FF_DEBUG("Found cached root key handle for %s -> %p", hKey2Str(result), result);
|
||||
return result;
|
||||
}
|
||||
|
||||
switch ((uintptr_t) hKey)
|
||||
{
|
||||
case (uintptr_t) HKEY_CURRENT_USER: {
|
||||
NTSTATUS status = RtlOpenCurrentUser(KEY_READ, &result);
|
||||
if (!NT_SUCCESS(status))
|
||||
{
|
||||
FF_DEBUG("RtlOpenCurrentUser() failed: %s", ffDebugNtStatus(status));
|
||||
return NULL;
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
case (uintptr_t) HKEY_LOCAL_MACHINE: {
|
||||
UNICODE_STRING path = RTL_CONSTANT_STRING(L"\\Registry\\Machine");
|
||||
NTSTATUS status = NtOpenKey(&result, KEY_READ, &(OBJECT_ATTRIBUTES) {
|
||||
.Length = sizeof(OBJECT_ATTRIBUTES),
|
||||
.RootDirectory = NULL,
|
||||
.ObjectName = &path,
|
||||
.Attributes = OBJ_CASE_INSENSITIVE,
|
||||
});
|
||||
if (!NT_SUCCESS(status))
|
||||
{
|
||||
FF_DEBUG("NtOpenKey(%ls) failed: %s (0x%08lx)", path.Buffer, ffDebugNtStatus(status), status);
|
||||
return NULL;
|
||||
}
|
||||
break;
|
||||
}
|
||||
default:
|
||||
// Unsupported
|
||||
FF_DEBUG("Unsupported root key: %p", hKey);
|
||||
assert(false);
|
||||
return NULL;
|
||||
}
|
||||
hRootKeys[(uintptr_t) hKey - (uintptr_t) HKEY_CLASSES_ROOT] = result;
|
||||
FF_DEBUG("Opened root key %s -> %p", hKey2Str(result), result);
|
||||
return result;
|
||||
}
|
||||
|
||||
bool ffRegOpenSubkeyForRead(HANDLE hKey, const wchar_t* subKeyW, HANDLE* result, FFstrbuf* error)
|
||||
{
|
||||
assert(hKey);
|
||||
assert(subKeyW);
|
||||
assert(result);
|
||||
|
||||
FF_DEBUG("Opening subkey %s\\%ls for read", hKey2Str(hKey), subKeyW);
|
||||
|
||||
USHORT subKeyLen = (USHORT) (wcslen(subKeyW) * sizeof(wchar_t));
|
||||
if (!NT_SUCCESS(NtOpenKey(result, KEY_READ, &(OBJECT_ATTRIBUTES) {
|
||||
.Length = sizeof(OBJECT_ATTRIBUTES),
|
||||
.RootDirectory = hKey,
|
||||
.ObjectName = &(UNICODE_STRING) {
|
||||
.Length = subKeyLen,
|
||||
.MaximumLength = subKeyLen + (USHORT) sizeof(wchar_t),
|
||||
.Buffer = (wchar_t*) subKeyW,
|
||||
},
|
||||
})))
|
||||
{
|
||||
FF_DEBUG("NtOpenKey(%s\\<subkey>) failed", hKey2Str(hKey));
|
||||
if (error)
|
||||
{
|
||||
FF_STRBUF_AUTO_DESTROY subKeyA = ffStrbufCreateWS(subKeyW);
|
||||
ffStrbufAppendF(error, "RegOpenKeyExW(%s\\%s) failed", hKey2Str(hKey), subKeyA.chars);
|
||||
ffStrbufAppendF(error, "NtOpenKey(%s\\%s) failed", hKey2Str(hKey), subKeyA.chars);
|
||||
}
|
||||
return false;
|
||||
}
|
||||
FF_DEBUG("Opened subkey under %s -> %p", hKey2Str(hKey), *result);
|
||||
return true;
|
||||
}
|
||||
|
||||
bool ffRegReadStrbuf(HKEY hKey, const wchar_t* valueNameW, FFstrbuf* result, FFstrbuf* error)
|
||||
static bool processRegValue(const FFRegValueArg* arg, const ULONG regType, const void* regData, ULONG regDataLen, FFstrbuf* error)
|
||||
{
|
||||
DWORD bufSize; //with tailing '\0'
|
||||
if(RegGetValueW(hKey, NULL, valueNameW, RRF_RT_REG_SZ, NULL, NULL, &bufSize) != ERROR_SUCCESS)
|
||||
switch (arg->type)
|
||||
{
|
||||
if(error)
|
||||
case FF_ARG_TYPE_STRBUF:
|
||||
{
|
||||
if(!valueNameW)
|
||||
valueNameW = L"(default)";
|
||||
FF_STRBUF_AUTO_DESTROY valueNameA = ffStrbufCreateWS(valueNameW);
|
||||
ffStrbufAppendF(error, "RegGetValueW(%s, NULL, RRF_RT_REG_SZ) failed", valueNameA.chars);
|
||||
if (regType != REG_SZ && regType != REG_EXPAND_SZ)
|
||||
goto type_mismatch;
|
||||
|
||||
FFstrbuf* strbuf = (FFstrbuf*) arg->value;
|
||||
uint32_t strLen = regDataLen / sizeof(wchar_t);
|
||||
if (strLen == 0)
|
||||
ffStrbufClear(strbuf);
|
||||
else
|
||||
{
|
||||
const wchar_t* ws = (const wchar_t*) regData;
|
||||
if (ws[strLen - 1] == L'\0')
|
||||
--strLen;
|
||||
ffStrbufSetNWS(strbuf, strLen, ws);
|
||||
}
|
||||
break;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
assert(bufSize >= sizeof(wchar_t));
|
||||
wchar_t* FF_AUTO_FREE resultW = (wchar_t*)malloc(bufSize);
|
||||
if(RegGetValueW(hKey, NULL, valueNameW, RRF_RT_REG_SZ, NULL, resultW, &bufSize) != ERROR_SUCCESS)
|
||||
{
|
||||
if(error)
|
||||
|
||||
case FF_ARG_TYPE_UINT:
|
||||
case FF_ARG_TYPE_UINT64:
|
||||
case FF_ARG_TYPE_UINT16:
|
||||
case FF_ARG_TYPE_UINT8:
|
||||
case FF_ARG_TYPE_BOOL:
|
||||
{
|
||||
if(!valueNameW)
|
||||
valueNameW = L"(default)";
|
||||
FF_STRBUF_AUTO_DESTROY valueNameA = ffStrbufCreateWS(valueNameW);
|
||||
ffStrbufAppendF(error, "RegGetValueW(%s, result, RRF_RT_REG_SZ) failed", valueNameA.chars);
|
||||
uint64_t value = 0;
|
||||
|
||||
if (regType == REG_DWORD)
|
||||
{
|
||||
if (regDataLen < sizeof(uint32_t))
|
||||
goto type_mismatch;
|
||||
value = *(uint32_t*) regData;
|
||||
}
|
||||
else if (regType == REG_QWORD)
|
||||
{
|
||||
if (regDataLen < sizeof(uint64_t))
|
||||
goto type_mismatch;
|
||||
value = *(uint64_t*) regData;
|
||||
}
|
||||
else
|
||||
goto type_mismatch;
|
||||
|
||||
if (arg->type == FF_ARG_TYPE_UINT) *(uint32_t*) arg->value = (uint32_t) value;
|
||||
else if (arg->type == FF_ARG_TYPE_UINT64) *(uint64_t*) arg->value = (uint64_t) value;
|
||||
else if (arg->type == FF_ARG_TYPE_UINT16) *(uint16_t*) arg->value = (uint16_t) value;
|
||||
else if (arg->type == FF_ARG_TYPE_UINT8) *(uint8_t*) arg->value = (uint8_t) value;
|
||||
else if (arg->type == FF_ARG_TYPE_BOOL) *(bool*) arg->value = value != 0;
|
||||
break;
|
||||
}
|
||||
return false;
|
||||
|
||||
case FF_ARG_TYPE_FLOAT:
|
||||
{
|
||||
if (regDataLen < sizeof(float))
|
||||
goto type_mismatch;
|
||||
*(float*) arg->value = *(float*) regData;
|
||||
break;
|
||||
}
|
||||
|
||||
case FF_ARG_TYPE_DOUBLE:
|
||||
{
|
||||
if (regDataLen < sizeof(double))
|
||||
goto type_mismatch;
|
||||
*(double*) arg->value = *(double*) regData;
|
||||
break;
|
||||
}
|
||||
|
||||
case FF_ARG_TYPE_LIST:
|
||||
{
|
||||
if (regType != REG_MULTI_SZ)
|
||||
goto type_mismatch;
|
||||
|
||||
FFlist* list = (FFlist*) arg->value;
|
||||
if (list->elementSize != sizeof(FFstrbuf))
|
||||
{
|
||||
if (error)
|
||||
{
|
||||
FF_STRBUF_AUTO_DESTROY nameA = arg->name ? ffStrbufCreateWS(arg->name) : ffStrbufCreateStatic("(default)");
|
||||
ffStrbufAppendF(error, "ffRegReadValues(%s) type mismatch: expected list of strbuf for REG_MULTI_SZ", nameA.chars);
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
ffListClear(list);
|
||||
|
||||
for (
|
||||
const wchar_t* ptr = (const wchar_t*) regData;
|
||||
(const uint8_t*) ptr < (const uint8_t*) regData + regDataLen && *ptr;
|
||||
ptr++
|
||||
)
|
||||
{
|
||||
uint32_t strLen = (uint32_t) wcsnlen(ptr, regDataLen / sizeof(wchar_t) - (size_t) (ptr - (const wchar_t*) regData));
|
||||
ffStrbufInitNWS(FF_LIST_ADD(FFstrbuf, *list), strLen, ptr);
|
||||
ptr += strLen;
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
case FF_ARG_TYPE_BUFFER:
|
||||
{
|
||||
if (regType != REG_BINARY)
|
||||
goto type_mismatch;
|
||||
|
||||
FFArgBuffer* buffer = (FFArgBuffer*) arg->value;
|
||||
if (buffer->length == 0)
|
||||
{
|
||||
buffer->data = malloc(regDataLen);
|
||||
}
|
||||
else if (buffer->length < regDataLen)
|
||||
{
|
||||
if (error)
|
||||
{
|
||||
FF_STRBUF_AUTO_DESTROY nameA = arg->name ? ffStrbufCreateWS(arg->name) : ffStrbufCreateStatic("(default)");
|
||||
ffStrbufAppendF(error, "ffRegReadValues(%s) buffer too small (%u): expected %u", nameA.chars, (unsigned) buffer->length, (unsigned) regDataLen);
|
||||
}
|
||||
return false;
|
||||
}
|
||||
buffer->length = regDataLen;
|
||||
memcpy(buffer->data, regData, regDataLen);
|
||||
break;
|
||||
}
|
||||
|
||||
case FF_ARG_TYPE_INT: // Use UINT instead
|
||||
case FF_ARG_TYPE_STRING:
|
||||
case FF_ARG_TYPE_NULL:
|
||||
default:
|
||||
if (error)
|
||||
{
|
||||
FF_STRBUF_AUTO_DESTROY nameA = arg->name ? ffStrbufCreateWS(arg->name) : ffStrbufCreateStatic("(default)");
|
||||
ffStrbufAppendF(error, "processRegValue(%s) unsupported FFArgType %u", nameA.chars, (unsigned) arg->type);
|
||||
}
|
||||
return false;
|
||||
}
|
||||
ffStrbufSetNWS(result, (uint32_t) (bufSize / sizeof(*resultW) - 1), resultW);
|
||||
|
||||
return true;
|
||||
|
||||
type_mismatch:
|
||||
FF_DEBUG("ffRegReadValues(%ls) type mismatch: regType=%u, argType=%u, dataLen=%u",
|
||||
arg->name ?: L"(default)", (unsigned) regType, (unsigned) arg->type, (unsigned) regDataLen);
|
||||
if (error)
|
||||
{
|
||||
FF_STRBUF_AUTO_DESTROY nameA = arg->name ? ffStrbufCreateWS(arg->name) : ffStrbufCreateStatic("(default)");
|
||||
ffStrbufAppendF(error, "ffRegReadValues(%s) type mismatch: regType=%u, argType=%u, dataLen=%u",
|
||||
nameA.chars, (unsigned) regType, (unsigned) arg->type, (unsigned) regDataLen);
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
bool ffRegReadData(HKEY hKey, const wchar_t* valueNameW, uint8_t** result, uint32_t* length, FFstrbuf* error)
|
||||
bool ffRegReadValue(HANDLE hKey, const FFRegValueArg* arg, FFstrbuf* error)
|
||||
{
|
||||
assert(result && length);
|
||||
DWORD bufSize = 0;
|
||||
LONG err = RegGetValueW(hKey, NULL, valueNameW, RRF_RT_REG_BINARY, NULL, NULL, &bufSize);
|
||||
if(err != ERROR_SUCCESS || bufSize == 0)
|
||||
{
|
||||
if(error)
|
||||
{
|
||||
if(!valueNameW)
|
||||
valueNameW = L"(default)";
|
||||
FF_STRBUF_AUTO_DESTROY valueNameA = ffStrbufCreateWS(valueNameW);
|
||||
ffStrbufAppendF(error, "RegGetValueW(%s, NULL, RRF_RT_REG_BINARY, NULL, NULL, &bufSize) failed", valueNameA.chars);
|
||||
}
|
||||
return false;
|
||||
}
|
||||
UNICODE_STRING* valueNameU = &(UNICODE_STRING) {
|
||||
.Length = arg->name ? (USHORT) (wcslen(arg->name) * sizeof(wchar_t)) : 0 /*(default)*/,
|
||||
.MaximumLength = 0,
|
||||
.Buffer = (wchar_t*) arg->name,
|
||||
};
|
||||
|
||||
uint8_t* buf = (uint8_t*) malloc(bufSize);
|
||||
err = RegGetValueW(hKey, NULL, valueNameW, RRF_RT_REG_BINARY, NULL, buf, &bufSize);
|
||||
if(err != ERROR_SUCCESS)
|
||||
{
|
||||
if(error)
|
||||
{
|
||||
if(!valueNameW)
|
||||
valueNameW = L"(default)";
|
||||
FF_STRBUF_AUTO_DESTROY valueNameA = ffStrbufCreateWS(valueNameW);
|
||||
ffStrbufAppendF(error, "RegGetValueW(%s, NULL, RRF_RT_REG_BINARY, NULL, length) failed", valueNameA.chars);
|
||||
}
|
||||
free(buf);
|
||||
return false;
|
||||
}
|
||||
*result = buf;
|
||||
*length = bufSize;
|
||||
return true;
|
||||
}
|
||||
alignas(KEY_VALUE_PARTIAL_INFORMATION) uint8_t staticBuffer[128 + sizeof(KEY_VALUE_PARTIAL_INFORMATION)];
|
||||
FF_AUTO_FREE uint8_t* dynamicBuffer = NULL;
|
||||
|
||||
bool ffRegReadUint(HKEY hKey, const wchar_t* valueNameW, uint32_t* result, FFstrbuf* error)
|
||||
{
|
||||
DWORD bufSize = sizeof(*result);
|
||||
if(RegGetValueW(hKey, NULL, valueNameW, RRF_RT_DWORD, NULL, result, &bufSize) != ERROR_SUCCESS)
|
||||
{
|
||||
if(error)
|
||||
{
|
||||
if(!valueNameW)
|
||||
valueNameW = L"(default)";
|
||||
FF_STRBUF_AUTO_DESTROY valueNameA = ffStrbufCreateWS(valueNameW);
|
||||
ffStrbufAppendF(error, "RegGetValueW(%s, result, RRF_RT_DWORD) failed", valueNameA.chars);
|
||||
}
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
KEY_VALUE_PARTIAL_INFORMATION* buffer = (KEY_VALUE_PARTIAL_INFORMATION*) &staticBuffer;
|
||||
DWORD bufSize = sizeof(staticBuffer);
|
||||
if (NT_SUCCESS(NtQueryValueKey(hKey, valueNameU, KeyValuePartialInformation, buffer, bufSize, &bufSize)))
|
||||
goto process_value;
|
||||
|
||||
bool ffRegReadUint64(HKEY hKey, const wchar_t* valueNameW, uint64_t* result, FFstrbuf* error)
|
||||
{
|
||||
DWORD bufSize = sizeof(*result);
|
||||
if(RegGetValueW(hKey, NULL, valueNameW, RRF_RT_QWORD, NULL, result, &bufSize) != ERROR_SUCCESS)
|
||||
{
|
||||
if(error)
|
||||
{
|
||||
if(!valueNameW)
|
||||
valueNameW = L"(default)";
|
||||
FF_STRBUF_AUTO_DESTROY valueNameA = ffStrbufCreateWS(valueNameW);
|
||||
ffStrbufAppendF(error, "RegGetValueW(%s, result, RRF_RT_QWORD) failed", valueNameA.chars);
|
||||
}
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
bool ffRegGetSubKey(HKEY hKey, uint32_t index, FFstrbuf* result, FFstrbuf* error)
|
||||
{
|
||||
DWORD bufSize = 0;
|
||||
if(RegQueryInfoKeyW(hKey, NULL, NULL, NULL, NULL, &bufSize, NULL, NULL, NULL, NULL, NULL, NULL) != ERROR_SUCCESS)
|
||||
if (bufSize == 0)
|
||||
{
|
||||
FF_DEBUG("NtQueryValueKey(%p, %ls) failed (bufSize=0)", hKey, arg->name ?: L"(default)");
|
||||
if (error)
|
||||
ffStrbufAppendS(error, "RegQueryInfoKeyW(hKey) failed");
|
||||
{
|
||||
FF_STRBUF_AUTO_DESTROY valueNameA = arg->name ? ffStrbufCreateWS(arg->name) : ffStrbufCreateStatic("(default)");
|
||||
ffStrbufAppendF(error, "NtQueryValueKey(%p, %s) failed", hKey, valueNameA.chars);
|
||||
}
|
||||
return false;
|
||||
}
|
||||
++bufSize;
|
||||
wchar_t* FF_AUTO_FREE resultW = (wchar_t*) malloc(bufSize * sizeof(*resultW));
|
||||
if(RegEnumKeyExW(hKey, index, resultW, &bufSize, NULL, NULL, NULL, NULL) != ERROR_SUCCESS)
|
||||
|
||||
dynamicBuffer = (uint8_t*) malloc(bufSize);
|
||||
buffer = (KEY_VALUE_PARTIAL_INFORMATION*) dynamicBuffer;
|
||||
|
||||
if (!NT_SUCCESS(NtQueryValueKey(hKey, valueNameU, KeyValuePartialInformation, buffer, bufSize, &bufSize)))
|
||||
{
|
||||
FF_DEBUG("NtQueryValueKey(%p, %ls, buffer=%u) failed", hKey, arg->name ?: L"(default)", (unsigned) bufSize);
|
||||
if (error)
|
||||
ffStrbufAppendF(error, "RegEnumKeyExW(hKey, %u) failed", (unsigned) index);
|
||||
{
|
||||
FF_STRBUF_AUTO_DESTROY valueNameA = arg->name ? ffStrbufCreateWS(arg->name) : ffStrbufCreateStatic("(default)");
|
||||
ffStrbufAppendF(error, "NtQueryValueKey(%p, %s, buffer) failed", hKey, valueNameA.chars);
|
||||
}
|
||||
return false;
|
||||
}
|
||||
ffStrbufSetWS(result, resultW);
|
||||
return true;
|
||||
|
||||
process_value:
|
||||
FF_DEBUG("Read value from %p (%ls), type=%u, len=%u", hKey, arg->name ?: L"(default)", (unsigned) buffer->Type, (unsigned) buffer->DataLength);
|
||||
return processRegValue(arg, buffer->Type, buffer->Data, buffer->DataLength, error);
|
||||
}
|
||||
|
||||
bool ffRegGetNSubKeys(HKEY hKey, uint32_t* result, FFstrbuf* error)
|
||||
bool ffRegReadValues(HANDLE hKey, uint32_t argc, const FFRegValueArg argv[], FFstrbuf* error)
|
||||
{
|
||||
DWORD buffer;
|
||||
if(RegQueryInfoKeyW(hKey, NULL, NULL, NULL, &buffer, NULL, NULL, NULL, NULL, NULL, NULL, NULL) != ERROR_SUCCESS)
|
||||
if (__builtin_expect(argc == 0, false))
|
||||
return true;
|
||||
|
||||
assert(argv);
|
||||
|
||||
FF_AUTO_FREE UNICODE_STRING* names = (UNICODE_STRING*) calloc(argc, sizeof(*names));
|
||||
FF_AUTO_FREE KEY_VALUE_ENTRY* entries = (KEY_VALUE_ENTRY*) calloc(argc, sizeof(*entries));
|
||||
|
||||
for (uint32_t i = 0; i < argc; ++i)
|
||||
{
|
||||
if (__builtin_expect(!argv[i].value, false))
|
||||
{
|
||||
FF_DEBUG("ffRegReadValues(argv[%u].value) is NULL", (unsigned) i);
|
||||
if (error) ffStrbufAppendF(error, "ffRegReadValues(argv[%u].pVar) is NULL", (unsigned) i);
|
||||
return false;
|
||||
}
|
||||
|
||||
names[i] = (UNICODE_STRING) {
|
||||
.Length = argv[i].name ? (USHORT) (wcslen(argv[i].name) * sizeof(wchar_t)) : 0 /*(default)*/,
|
||||
.MaximumLength = 0,
|
||||
.Buffer = (wchar_t*) argv[i].name,
|
||||
};
|
||||
entries[i].ValueName = &names[i];
|
||||
}
|
||||
|
||||
ULONG bufferSize = argc * 128;
|
||||
if (bufferSize < 512)
|
||||
bufferSize = 512;
|
||||
|
||||
FF_AUTO_FREE uint8_t* buffer = NULL;
|
||||
|
||||
while (true)
|
||||
{
|
||||
buffer = (uint8_t*) realloc(buffer, bufferSize);
|
||||
|
||||
ULONG writtenSize = bufferSize;
|
||||
ULONG requiredSize = 0;
|
||||
NTSTATUS status = NtQueryMultipleValueKey(hKey, entries, argc, buffer, &writtenSize, &requiredSize);
|
||||
|
||||
if (!NT_SUCCESS(status))
|
||||
{
|
||||
// Buffer too small: docs guarantee requiredSize is returned when provided.
|
||||
if (requiredSize > bufferSize)
|
||||
{
|
||||
FF_DEBUG("NtQueryMultipleValueKey(%p) resize buffer: %u -> %u", hKey, (unsigned) bufferSize, (unsigned) requiredSize);
|
||||
bufferSize = requiredSize;
|
||||
continue;
|
||||
}
|
||||
|
||||
FF_DEBUG("NtQueryMultipleValueKey(%p, argc=%u) failed, status=0x%08X", hKey, (unsigned) argc, (unsigned) status);
|
||||
if (error)
|
||||
ffStrbufAppendF(error, "NtQueryMultipleValueKey(%p, argc=%u) failed, status=0x%08X",
|
||||
hKey, (unsigned) argc, (unsigned) status);
|
||||
return false;
|
||||
}
|
||||
|
||||
break;
|
||||
}
|
||||
|
||||
for (uint32_t i = 0; i < argc; ++i)
|
||||
{
|
||||
const FFRegValueArg* arg = &argv[i];
|
||||
const KEY_VALUE_ENTRY* entry = &entries[i];
|
||||
|
||||
FF_DEBUG("Read value[%u] from %p: type=%u, len=%u", (unsigned) i, hKey, (unsigned) entry->Type, (unsigned) entry->DataLength);
|
||||
if (!processRegValue(arg, entry->Type, buffer + entry->DataOffset, entry->DataLength, error))
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool ffRegGetSubKey(HANDLE hKey, uint32_t index, FFstrbuf* result, FFstrbuf* error)
|
||||
{
|
||||
assert(hKey);
|
||||
assert(result);
|
||||
|
||||
alignas(KEY_BASIC_INFORMATION) uint8_t buffer[sizeof(KEY_BASIC_INFORMATION) + MAX_PATH * sizeof(wchar_t)];
|
||||
ULONG bufSize = (ULONG) sizeof(buffer);
|
||||
KEY_BASIC_INFORMATION* keyInfo = (KEY_BASIC_INFORMATION*) buffer;
|
||||
|
||||
if (!NT_SUCCESS(NtEnumerateKey(hKey, index, KeyBasicInformation, keyInfo, bufSize, &bufSize)))
|
||||
{
|
||||
FF_DEBUG("NtEnumerateKey(hKey=%p, index=%u) failed", hKey, (unsigned) index);
|
||||
if (error)
|
||||
ffStrbufAppendS(error, "RegQueryInfoKeyW(hKey) failed");
|
||||
ffStrbufAppendF(error, "NtEnumerateKey(hKey=%p, %u, keyInfo) failed", hKey, (unsigned) index);
|
||||
return false;
|
||||
}
|
||||
|
||||
*result = buffer;
|
||||
ffStrbufSetNWS(result, keyInfo->NameLength / sizeof(wchar_t), keyInfo->Name);
|
||||
return true;
|
||||
}
|
||||
|
||||
bool ffRegGetNSubKeys(HANDLE hKey, uint32_t* result, FFstrbuf* error)
|
||||
{
|
||||
assert(hKey);
|
||||
assert(result);
|
||||
|
||||
alignas(KEY_FULL_INFORMATION) uint8_t buffer[sizeof(KEY_FULL_INFORMATION) + MAX_PATH * sizeof(wchar_t)];
|
||||
ULONG bufSize = sizeof(buffer);
|
||||
KEY_FULL_INFORMATION* keyInfo = (KEY_FULL_INFORMATION*) buffer;
|
||||
|
||||
if (!NT_SUCCESS(NtQueryKey(hKey, KeyFullInformation, keyInfo, bufSize, &bufSize)))
|
||||
{
|
||||
FF_DEBUG("NtQueryKey(hKey=%p, KeyFullInformation) failed", hKey);
|
||||
if (error)
|
||||
ffStrbufAppendF(error, "NtQueryKey(hKey=%p, KeyFullInformation, keyInfo) failed", hKey);
|
||||
return false;
|
||||
}
|
||||
|
||||
*result = (uint32_t) keyInfo->SubKeys;
|
||||
return true;
|
||||
}
|
||||
|
||||
@@ -1,21 +1,68 @@
|
||||
#pragma once
|
||||
|
||||
#include "fastfetch.h"
|
||||
#include "common/argType.h"
|
||||
#include "common/io.h"
|
||||
|
||||
#include <windows.h>
|
||||
#ifndef HKEY_CURRENT_USER
|
||||
#define HKEY_CLASSES_ROOT ((HKEY) (ULONG_PTR)((LONG)0x80000000))
|
||||
#define HKEY_CURRENT_USER ((HKEY) (ULONG_PTR)((LONG)0x80000001))
|
||||
#define HKEY_LOCAL_MACHINE ((HKEY) (ULONG_PTR)((LONG)0x80000002))
|
||||
#define HKEY_USERS ((HKEY) (ULONG_PTR)((LONG)0x80000003))
|
||||
#define HKEY_PERFORMANCE_DATA ((HKEY) (ULONG_PTR)((LONG)0x80000004))
|
||||
#define HKEY_CURRENT_CONFIG ((HKEY) (ULONG_PTR)((LONG)0x80000005))
|
||||
#define HKEY_DYN_DATA ((HKEY) (ULONG_PTR)((LONG)0x80000006))
|
||||
#define HKEY_CURRENT_USER_LOCAL_SETTINGS ((HKEY) (ULONG_PTR)((LONG)0x80000007))
|
||||
#endif
|
||||
|
||||
static inline void wrapRegCloseKey(HKEY* phKey)
|
||||
typedef struct FFRegValueArg
|
||||
{
|
||||
if(*phKey)
|
||||
RegCloseKey(*phKey);
|
||||
FFArgType type;
|
||||
const void* value;
|
||||
const wchar_t* name;
|
||||
} FFRegValueArg;
|
||||
|
||||
HANDLE ffRegGetRootKeyHandle(HKEY hKey);
|
||||
bool ffRegOpenSubkeyForRead(HANDLE hKey, const wchar_t* subKeyW, HANDLE* result, FFstrbuf* error);
|
||||
bool ffRegReadValue(HANDLE hKey, const FFRegValueArg* arg, FFstrbuf* error);
|
||||
bool ffRegReadValues(HANDLE hKey, uint32_t argc, const FFRegValueArg argv[], FFstrbuf* error);
|
||||
bool ffRegGetSubKey(HANDLE hKey, uint32_t index, FFstrbuf* result, FFstrbuf* error);
|
||||
bool ffRegGetNSubKeys(HANDLE hKey, uint32_t* result, FFstrbuf* error);
|
||||
|
||||
static inline bool ffRegOpenKeyForRead(HKEY hRootKey, const wchar_t* subKeyW, HANDLE* result, FFstrbuf* error)
|
||||
{
|
||||
return ffRegOpenSubkeyForRead(ffRegGetRootKeyHandle(hRootKey), subKeyW, result, error);
|
||||
}
|
||||
|
||||
#define FF_HKEY_AUTO_DESTROY HKEY __attribute__((__cleanup__(wrapRegCloseKey)))
|
||||
|
||||
bool ffRegOpenKeyForRead(HKEY hKey, const wchar_t* subKeyW, HKEY* result, FFstrbuf* error);
|
||||
bool ffRegReadStrbuf(HKEY hKey, const wchar_t* valueNameW, FFstrbuf* result, FFstrbuf* error);
|
||||
bool ffRegReadData(HKEY hKey, const wchar_t* valueNameW, uint8_t** result, uint32_t* length, FFstrbuf* error);
|
||||
bool ffRegReadUint(HKEY hKey, const wchar_t* valueNameW, uint32_t* result, FFstrbuf* error);
|
||||
bool ffRegReadUint64(HKEY hKey, const wchar_t* valueNameW, uint64_t* result, FFstrbuf* error);
|
||||
bool ffRegGetSubKey(HKEY hKey, uint32_t index, FFstrbuf* result, FFstrbuf* error);
|
||||
bool ffRegGetNSubKeys(HKEY hKey, uint32_t* result, FFstrbuf* error);
|
||||
static inline bool ffRegReadStrbuf(HANDLE hKey, const wchar_t* valueNameW, FFstrbuf* result, FFstrbuf* error)
|
||||
{
|
||||
return ffRegReadValue(hKey, &(FFRegValueArg) {
|
||||
.type = FF_ARG_TYPE_STRBUF,
|
||||
.value = result,
|
||||
.name = valueNameW,
|
||||
}, error);
|
||||
}
|
||||
static inline bool ffRegReadUint(HANDLE hKey, const wchar_t* valueNameW, uint32_t* result, FFstrbuf* error)
|
||||
{
|
||||
return ffRegReadValue(hKey, &(FFRegValueArg) {
|
||||
.type = FF_ARG_TYPE_UINT,
|
||||
.value = result,
|
||||
.name = valueNameW,
|
||||
}, error);
|
||||
}
|
||||
static inline bool ffRegReadUint64(HANDLE hKey, const wchar_t* valueNameW, uint64_t* result, FFstrbuf* error)
|
||||
{
|
||||
return ffRegReadValue(hKey, &(FFRegValueArg) {
|
||||
.type = FF_ARG_TYPE_UINT64,
|
||||
.value = result,
|
||||
.name = valueNameW,
|
||||
}, error);
|
||||
}
|
||||
static inline bool ffRegReadData(HANDLE hKey, const wchar_t* valueNameW, FFArgBuffer* buffer, FFstrbuf* error)
|
||||
{
|
||||
return ffRegReadValue(hKey, &(FFRegValueArg) {
|
||||
.type = FF_ARG_TYPE_BUFFER,
|
||||
.value = buffer,
|
||||
.name = valueNameW,
|
||||
}, error);
|
||||
}
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
#include "unicode.h"
|
||||
|
||||
#include <windows.h>
|
||||
#include "common/windows/nt.h"
|
||||
|
||||
void ffStrbufSetNWS(FFstrbuf* result, uint32_t length, const wchar_t* source)
|
||||
{
|
||||
@@ -10,15 +10,19 @@ void ffStrbufSetNWS(FFstrbuf* result, uint32_t length, const wchar_t* source)
|
||||
return;
|
||||
}
|
||||
|
||||
int size_needed = WideCharToMultiByte(CP_UTF8, 0, source, (int)length, NULL, 0, NULL, NULL);
|
||||
if (size_needed <= 0)
|
||||
ULONG size_needed = 0;
|
||||
NTSTATUS status = RtlUnicodeToUTF8N(NULL, 0, &size_needed, source, length * sizeof(wchar_t));
|
||||
|
||||
if (size_needed == 0)
|
||||
{
|
||||
ffStrbufSetF(result, "WCTMB failed: %u", (unsigned) GetLastError());
|
||||
ffStrbufSetF(result, "RtlUnicodeToUTF8N failed: %X", (unsigned) status);
|
||||
return;
|
||||
}
|
||||
ffStrbufEnsureFixedLengthFree(result, (uint32_t)size_needed);
|
||||
WideCharToMultiByte(CP_UTF8, 0, source, (int)length, result->chars, size_needed, NULL, NULL);
|
||||
result->length = (uint32_t)size_needed;
|
||||
|
||||
ffStrbufEnsureFixedLengthFree(result, size_needed);
|
||||
RtlUnicodeToUTF8N(result->chars, size_needed, &size_needed, source, length * sizeof(wchar_t));
|
||||
|
||||
result->length = size_needed;
|
||||
result->chars[size_needed] = '\0';
|
||||
}
|
||||
|
||||
@@ -27,12 +31,18 @@ void ffStrbufAppendNWS(FFstrbuf* result, uint32_t length, const wchar_t* source)
|
||||
if(!length)
|
||||
return;
|
||||
|
||||
int size_needed = WideCharToMultiByte(CP_UTF8, 0, source, (int)length, NULL, 0, NULL, NULL);
|
||||
if (size_needed <= 0)
|
||||
return;
|
||||
ULONG size_needed = 0;
|
||||
NTSTATUS status = RtlUnicodeToUTF8N(NULL, 0, &size_needed, source, length * sizeof(wchar_t));
|
||||
|
||||
ffStrbufEnsureFree(result, (uint32_t)size_needed);
|
||||
WideCharToMultiByte(CP_UTF8, 0, source, (int)length, result->chars + result->length, size_needed, NULL, NULL);
|
||||
result->length += (uint32_t)size_needed;
|
||||
if (size_needed == 0)
|
||||
{
|
||||
ffStrbufAppendF(result, "RtlUnicodeToUTF8N failed: %X", (unsigned) status);
|
||||
return;
|
||||
}
|
||||
|
||||
ffStrbufEnsureFree(result, size_needed);
|
||||
RtlUnicodeToUTF8N(result->chars + result->length, size_needed, &size_needed, source, length * sizeof(wchar_t));
|
||||
|
||||
result->length += size_needed;
|
||||
result->chars[result->length] = '\0';
|
||||
}
|
||||
|
||||
@@ -1,49 +1,92 @@
|
||||
#include "common/windows/version.h"
|
||||
#include "common/debug.h"
|
||||
#include "common/mallocHelper.h"
|
||||
#include "common/windows/version.h"
|
||||
#include "common/windows/unicode.h"
|
||||
|
||||
#include <windows.h>
|
||||
|
||||
#define FF_VERSION_LANG_EN_US L"040904b0"
|
||||
|
||||
bool ffGetFileVersion(const wchar_t* filePath, const wchar_t* stringName, FFstrbuf* version)
|
||||
{
|
||||
FF_DEBUG("ffGetFileVersion: enter filePath=%ls stringName=%ls", filePath, stringName);
|
||||
|
||||
DWORD handle;
|
||||
DWORD size = GetFileVersionInfoSizeW(filePath, &handle);
|
||||
if (size > 0)
|
||||
if (size == 0)
|
||||
{
|
||||
FF_AUTO_FREE void *versionData = malloc(size);
|
||||
if (GetFileVersionInfoW(filePath, handle, size, versionData))
|
||||
{
|
||||
if (!stringName)
|
||||
{
|
||||
VS_FIXEDFILEINFO* verInfo;
|
||||
UINT len;
|
||||
if (VerQueryValueW(versionData, L"\\", (void **)&verInfo, &len) && len && verInfo->dwSignature == 0xFEEF04BD)
|
||||
{
|
||||
ffStrbufSetF(version, "%u.%u.%u.%u",
|
||||
(unsigned)((verInfo->dwProductVersionMS >> 16) & 0xffff),
|
||||
(unsigned)((verInfo->dwProductVersionMS >> 0) & 0xffff),
|
||||
(unsigned)((verInfo->dwProductVersionLS >> 16) & 0xffff),
|
||||
(unsigned)((verInfo->dwProductVersionLS >> 0) & 0xffff));
|
||||
return true;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
wchar_t* value;
|
||||
UINT valueLen;
|
||||
|
||||
wchar_t subBlock[128] = L"\\StringFileInfo\\" FF_VERSION_LANG_EN_US L"\\";
|
||||
wcscat_s(subBlock, ARRAY_SIZE(subBlock), stringName);
|
||||
if (VerQueryValueW(versionData, subBlock, (void **)&value, &valueLen) && valueLen > 0)
|
||||
{
|
||||
ffStrbufSetWS(version, value);
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
DWORD err = GetLastError();
|
||||
FF_DEBUG("GetFileVersionInfoSizeW failed: err=%lu (%s)",
|
||||
(unsigned long) err, ffDebugWin32Error(err));
|
||||
return false;
|
||||
}
|
||||
|
||||
FF_DEBUG("GetFileVersionInfoSizeW ok: size=%lu handle=%lu",
|
||||
(unsigned long) size, (unsigned long) handle);
|
||||
|
||||
FF_AUTO_FREE void* versionData = malloc(size);
|
||||
if (!versionData)
|
||||
{
|
||||
FF_DEBUG("malloc failed: size=%lu", (unsigned long) size);
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!GetFileVersionInfoW(filePath, handle, size, versionData))
|
||||
{
|
||||
DWORD err = GetLastError();
|
||||
FF_DEBUG("GetFileVersionInfoW failed: err=%lu (%s)",
|
||||
(unsigned long) err, ffDebugWin32Error(err));
|
||||
return false;
|
||||
}
|
||||
|
||||
FF_DEBUG("GetFileVersionInfoW ok");
|
||||
|
||||
if (!stringName)
|
||||
{
|
||||
VS_FIXEDFILEINFO* verInfo;
|
||||
UINT len;
|
||||
if (VerQueryValueW(versionData, L"\\", (void**) &verInfo, &len) &&
|
||||
len &&
|
||||
verInfo->dwSignature == 0xFEEF04BD)
|
||||
{
|
||||
ffStrbufSetF(version, "%u.%u.%u.%u",
|
||||
(unsigned) ((verInfo->dwProductVersionMS >> 16) & 0xffff),
|
||||
(unsigned) ((verInfo->dwProductVersionMS >> 0) & 0xffff),
|
||||
(unsigned) ((verInfo->dwProductVersionLS >> 16) & 0xffff),
|
||||
(unsigned) ((verInfo->dwProductVersionLS >> 0) & 0xffff));
|
||||
FF_DEBUG("fixed version resolved: %s", version->chars);
|
||||
return true;
|
||||
}
|
||||
|
||||
FF_DEBUG("fixed version query failed or invalid signature");
|
||||
return false;
|
||||
}
|
||||
|
||||
struct { WORD language; WORD codePage; }* translations;
|
||||
UINT translationsLen;
|
||||
|
||||
if (VerQueryValueW(versionData, L"\\VarFileInfo\\Translation", (void**) &translations, &translationsLen) &&
|
||||
translationsLen >= sizeof(*translations))
|
||||
{
|
||||
wchar_t subBlock[128];
|
||||
snwprintf(subBlock, ARRAY_SIZE(subBlock), L"\\StringFileInfo\\%04x%04x\\%ls",
|
||||
translations[0].language, translations[0].codePage, stringName);
|
||||
FF_DEBUG("query version string with translation: %ls", subBlock);
|
||||
|
||||
wchar_t* value;
|
||||
UINT valueLen; // Number of characters, including null terminator
|
||||
if (VerQueryValueW(versionData, subBlock, (void**) &value, &valueLen) && valueLen > 0)
|
||||
{
|
||||
ffStrbufSetNWS(version, valueLen - 1, value);
|
||||
FF_DEBUG("version string resolved (translation fallback): %s", version->chars);
|
||||
return true;
|
||||
}
|
||||
|
||||
FF_DEBUG("translation fallback query failed");
|
||||
}
|
||||
else
|
||||
{
|
||||
FF_DEBUG("no translation table found in version resource");
|
||||
}
|
||||
|
||||
FF_DEBUG("ffGetFileVersion failed");
|
||||
return false;
|
||||
}
|
||||
|
||||
@@ -29,20 +29,18 @@ const char* ffDetectBios(FFBiosResult* bios)
|
||||
if (!device)
|
||||
return "IODeviceTree:/ not found";
|
||||
|
||||
FF_CFTYPE_AUTO_RELEASE CFMutableDictionaryRef deviceProps = NULL;
|
||||
if(IORegistryEntryCreateCFProperties(device, &deviceProps, kCFAllocatorDefault, kNilOptions) != kIOReturnSuccess)
|
||||
return "IORegistryEntryCreateCFProperties(device) failed";
|
||||
|
||||
ffCfDictGetString(deviceProps, CFSTR("manufacturer"), &bios->vendor);
|
||||
ffCfDictGetString(deviceProps, CFSTR("time-stamp"), &bios->date);
|
||||
FF_CFTYPE_AUTO_RELEASE CFDataRef manufacturer = IORegistryEntryCreateCFProperty(device, CFSTR("manufacturer"), kCFAllocatorDefault, kNilOptions);
|
||||
ffCfStrGetString(manufacturer, &bios->vendor);
|
||||
FF_CFTYPE_AUTO_RELEASE CFDataRef timeStamp = IORegistryEntryCreateCFProperty(device, CFSTR("time-stamp"), kCFAllocatorDefault, kNilOptions);
|
||||
ffCfStrGetString(timeStamp, &bios->date);
|
||||
|
||||
FF_IOOBJECT_AUTO_RELEASE io_registry_entry_t deviceChosen = IORegistryEntryFromPath(MACH_PORT_NULL, "IODeviceTree:/chosen");
|
||||
if (deviceChosen)
|
||||
{
|
||||
FF_CFTYPE_AUTO_RELEASE CFStringRef systemFirmWareVersion = IORegistryEntryCreateCFProperty(deviceChosen, CFSTR("system-firmware-version"), kCFAllocatorDefault, kNilOptions);
|
||||
if (systemFirmWareVersion)
|
||||
FF_CFTYPE_AUTO_RELEASE CFStringRef systemFirmwareVersion = IORegistryEntryCreateCFProperty(deviceChosen, CFSTR("system-firmware-version"), kCFAllocatorDefault, kNilOptions);
|
||||
if (systemFirmwareVersion)
|
||||
{
|
||||
ffCfStrGetString(systemFirmWareVersion, &bios->version);
|
||||
ffCfStrGetString(systemFirmwareVersion, &bios->version);
|
||||
uint32_t index = ffStrbufFirstIndexC(&bios->version, '-');
|
||||
if (index != bios->version.length)
|
||||
{
|
||||
|
||||
@@ -2,14 +2,20 @@
|
||||
#include "common/io.h"
|
||||
#include "common/smbiosHelper.h"
|
||||
|
||||
#include <stdlib.h>
|
||||
|
||||
const char* ffDetectBios(FFBiosResult* bios)
|
||||
{
|
||||
ffGetSmbiosValue("/sys/devices/virtual/dmi/id/bios_date", "/sys/class/dmi/id/bios_date", &bios->date);
|
||||
ffGetSmbiosValue("/sys/devices/virtual/dmi/id/bios_release", "/sys/class/dmi/id/bios_release", &bios->release);
|
||||
ffGetSmbiosValue("/sys/devices/virtual/dmi/id/bios_vendor", "/sys/class/dmi/id/bios_vendor", &bios->vendor);
|
||||
ffGetSmbiosValue("/sys/devices/virtual/dmi/id/bios_version", "/sys/class/dmi/id/bios_version", &bios->version);
|
||||
if (ffGetSmbiosValue("/sys/devices/virtual/dmi/id/bios_date", "/sys/class/dmi/id/bios_date", &bios->date))
|
||||
{
|
||||
ffGetSmbiosValue("/sys/devices/virtual/dmi/id/bios_release", "/sys/class/dmi/id/bios_release", &bios->release);
|
||||
ffGetSmbiosValue("/sys/devices/virtual/dmi/id/bios_vendor", "/sys/class/dmi/id/bios_vendor", &bios->vendor);
|
||||
ffGetSmbiosValue("/sys/devices/virtual/dmi/id/bios_version", "/sys/class/dmi/id/bios_version", &bios->version);
|
||||
}
|
||||
else if (ffReadFileBuffer("/proc/device-tree/chosen/u-boot,version", &bios->version))
|
||||
{
|
||||
ffStrbufTrimRight(&bios->version, '\0');
|
||||
ffStrbufSetStatic(&bios->vendor, "U-Boot");
|
||||
}
|
||||
|
||||
if (ffPathExists("/sys/firmware/efi/", FF_PATHTYPE_DIRECTORY) || ffPathExists("/sys/firmware/acpi/tables/UEFI", FF_PATHTYPE_FILE))
|
||||
ffStrbufSetStatic(&bios->type, "UEFI");
|
||||
else
|
||||
|
||||
@@ -11,10 +11,12 @@
|
||||
|
||||
#include <fcntl.h>
|
||||
#include <unistd.h>
|
||||
|
||||
#elif __sun
|
||||
#include <libdevinfo.h>
|
||||
#include <sys/sunddi.h>
|
||||
#elif __APPLE__
|
||||
#include "common/apple/cf_helpers.h"
|
||||
#include <IOKit/IOKitLib.h>
|
||||
#endif
|
||||
|
||||
typedef struct FFSmbiosBios
|
||||
@@ -90,8 +92,12 @@ const char* ffDetectBios(FFBiosResult* bios)
|
||||
}
|
||||
di_fini(rootNode);
|
||||
#elif __HAIKU__ || __OpenBSD__
|
||||
// Currently SMBIOS detection is supported in legancy BIOS only
|
||||
// Currently SMBIOS detection is supported in legacy BIOS only
|
||||
ffStrbufSetStatic(&bios->type, "BIOS");
|
||||
#elif __APPLE__
|
||||
// Intel Macs use UEFI from day one
|
||||
FF_IOOBJECT_AUTO_RELEASE io_registry_entry_t deviceEfi = IORegistryEntryFromPath(MACH_PORT_NULL, "IODeviceTree:/efi");
|
||||
ffStrbufSetStatic(&bios->type, deviceEfi ? "UEFI" : "BIOS");
|
||||
#endif
|
||||
|
||||
return NULL;
|
||||
|
||||
@@ -9,14 +9,7 @@
|
||||
|
||||
const char* ffDetectBluetooth(FFBluetoothOptions* options, FFlist* devices /* FFBluetoothResult */)
|
||||
{
|
||||
// Actually bluetoothapis.dll, but it's missing on Windows 7
|
||||
FF_LIBRARY_LOAD_MESSAGE(bluetoothapis,
|
||||
#if FF_WIN7_COMPAT
|
||||
"bthprops.cpl"
|
||||
#else
|
||||
"bluetoothapis.dll"
|
||||
#endif
|
||||
, 1)
|
||||
FF_LIBRARY_LOAD_MESSAGE(bluetoothapis, "bluetoothapis.dll", 1)
|
||||
FF_LIBRARY_LOAD_SYMBOL_MESSAGE(bluetoothapis, BluetoothFindFirstDevice)
|
||||
FF_LIBRARY_LOAD_SYMBOL_MESSAGE(bluetoothapis, BluetoothFindNextDevice)
|
||||
FF_LIBRARY_LOAD_SYMBOL_MESSAGE(bluetoothapis, BluetoothFindDeviceClose)
|
||||
|
||||
@@ -54,13 +54,7 @@ static_assert(sizeof(BTH_LOCAL_RADIO_INFO) == 292, "BTH_LOCAL_RADIO_INFO should
|
||||
const char* ffDetectBluetoothRadio(FFlist* devices /* FFBluetoothRadioResult */)
|
||||
{
|
||||
// Actually bluetoothapis.dll, but it's missing on Windows 7
|
||||
FF_LIBRARY_LOAD_MESSAGE(bluetoothapis,
|
||||
#if FF_WIN7_COMPAT
|
||||
"bthprops.cpl"
|
||||
#else
|
||||
"bluetoothapis.dll"
|
||||
#endif
|
||||
, 1)
|
||||
FF_LIBRARY_LOAD_MESSAGE(bluetoothapis, "bluetoothapis.dll", 1)
|
||||
FF_LIBRARY_LOAD_SYMBOL_MESSAGE(bluetoothapis, BluetoothFindFirstRadio)
|
||||
FF_LIBRARY_LOAD_SYMBOL_MESSAGE(bluetoothapis, BluetoothFindNextRadio)
|
||||
FF_LIBRARY_LOAD_SYMBOL_MESSAGE(bluetoothapis, BluetoothFindRadioClose)
|
||||
@@ -104,7 +98,7 @@ const char* ffDetectBluetoothRadio(FFlist* devices /* FFBluetoothRadioResult */)
|
||||
device->connectable = ffBluetoothIsConnectable(hRadio);
|
||||
device->discoverable = ffBluetoothIsDiscoverable(hRadio);
|
||||
|
||||
CloseHandle(hRadio);
|
||||
NtClose(hRadio);
|
||||
} while (ffBluetoothFindNextRadio(hFind, &hRadio));
|
||||
|
||||
ffBluetoothFindRadioClose(hFind);
|
||||
|
||||
@@ -3,13 +3,14 @@
|
||||
#include "common/io.h"
|
||||
#include "common/windows/nt.h"
|
||||
|
||||
#include <ntstatus.h>
|
||||
#include <windows.h>
|
||||
|
||||
const char* enablePrivilege(const wchar_t* privilege)
|
||||
{
|
||||
FF_AUTO_CLOSE_FD HANDLE token = NULL;
|
||||
if (!OpenProcessToken(NtCurrentProcess(), TOKEN_ADJUST_PRIVILEGES | TOKEN_QUERY, &token))
|
||||
return "OpenProcessToken() failed";
|
||||
if (!NT_SUCCESS(NtOpenProcessToken(NtCurrentProcess(), TOKEN_ADJUST_PRIVILEGES, &token)))
|
||||
return "NtOpenProcessToken() failed";
|
||||
|
||||
TOKEN_PRIVILEGES tp = {
|
||||
.PrivilegeCount = 1,
|
||||
@@ -20,19 +21,21 @@ const char* enablePrivilege(const wchar_t* privilege)
|
||||
if (!LookupPrivilegeValueW(NULL, privilege, &tp.Privileges[0].Luid))
|
||||
return "LookupPrivilegeValue() failed";
|
||||
|
||||
if (!AdjustTokenPrivileges(token, false, &tp, sizeof(tp), NULL, NULL))
|
||||
return "AdjustTokenPrivileges() failed";
|
||||
NTSTATUS status = NtAdjustPrivilegesToken(token, false, &tp, sizeof(tp), NULL, NULL);
|
||||
if (!NT_SUCCESS(status))
|
||||
return "NtAdjustPrivilegesToken() failed";
|
||||
|
||||
if (GetLastError() == ERROR_NOT_ALL_ASSIGNED)
|
||||
return "The token does not have the specified privilege";
|
||||
if (status == STATUS_NOT_ALL_ASSIGNED)
|
||||
return "The token does not have the specified privilege; try sudo please";
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
const char* ffDetectBootmgr(FFBootmgrResult* result)
|
||||
{
|
||||
if (enablePrivilege(L"SeSystemEnvironmentPrivilege") != NULL)
|
||||
return "Failed to enable SeSystemEnvironmentPrivilege";
|
||||
const char* err = enablePrivilege(L"SeSystemEnvironmentPrivilege");
|
||||
if (err != NULL)
|
||||
return err;
|
||||
|
||||
GUID efiGlobalGuid;
|
||||
if (!NT_SUCCESS(RtlGUIDFromString(&(UNICODE_STRING) RTL_CONSTANT_STRING(L"{" FF_EFI_GLOBAL_GUID L"}"), &efiGlobalGuid)))
|
||||
|
||||
@@ -110,26 +110,29 @@ static const char* detectWithDdcci(FF_MAYBE_UNUSED FFBrightnessOptions* options,
|
||||
FF_LIBRARY_LOAD_SYMBOL_MESSAGE(libddcutil, ddca_close_display)
|
||||
|
||||
#ifndef FF_DISABLE_DLOPEN
|
||||
__typeof__(&ddca_init) ffddca_init = dlsym(libddcutil, "ddca_init");
|
||||
FF_LIBRARY_LOAD_SYMBOL_LAZY(libddcutil, ddca_init)
|
||||
if (ffddca_init)
|
||||
#else
|
||||
__typeof__(&ddca_init) ffddca_init = ddca_init;
|
||||
#endif
|
||||
{
|
||||
FF_SUPPRESS_IO();
|
||||
// Ref: https://github.com/rockowitz/ddcutil/issues/344
|
||||
if (ffddca_init(NULL, -1 /*DDCA_SYSLOG_NOT_SET*/, 1 /*DDCA_INIT_OPTIONS_DISABLE_CONFIG_FILE*/) < 0)
|
||||
return "ddca_init() failed";
|
||||
}
|
||||
#ifndef FF_DISABLE_DLOPEN
|
||||
else
|
||||
{
|
||||
__typeof__(&ddca_set_default_sleep_multiplier) ffddca_set_default_sleep_multiplier = dlsym(libddcutil, "ddca_set_default_sleep_multiplier");
|
||||
FF_LIBRARY_LOAD_SYMBOL_LAZY(libddcutil, ddca_set_default_sleep_multiplier);
|
||||
if (ffddca_set_default_sleep_multiplier)
|
||||
ffddca_set_default_sleep_multiplier(options->ddcciSleep / 40.0);
|
||||
|
||||
libddcutil = NULL; // Don't dlclose libddcutil. See https://github.com/rockowitz/ddcutil/issues/330
|
||||
}
|
||||
#else
|
||||
#if DDCUTIL_VMAJOR >= 2
|
||||
if (ddca_init(NULL, -1 /*DDCA_SYSLOG_NOT_SET*/, 1 /*DDCA_INIT_OPTIONS_DISABLE_CONFIG_FILE*/) < 0)
|
||||
return "ddca_init() failed";
|
||||
#else
|
||||
ddca_set_default_sleep_multiplier(options->ddcciSleep / 40.0);
|
||||
#endif
|
||||
#endif
|
||||
|
||||
FF_AUTO_FREE DDCA_Display_Info_List* infoList = NULL;
|
||||
|
||||
@@ -7,8 +7,37 @@ extern "C"
|
||||
#include "common/windows/wmi.hpp"
|
||||
#include "common/windows/unicode.hpp"
|
||||
|
||||
#include <highlevelmonitorconfigurationapi.h>
|
||||
#include <physicalmonitorenumerationapi.h>
|
||||
#include <winternl.h>
|
||||
|
||||
NTSYSAPI NTSTATUS WINAPI GetPhysicalMonitors(
|
||||
_In_ UNICODE_STRING *pstrDeviceName,
|
||||
_In_ DWORD dwPhysicalMonitorArraySize,
|
||||
_Out_ DWORD *pdwNumPhysicalMonitorHandlesInArray,
|
||||
_Out_ HANDLE *phPhysicalMonitorArray
|
||||
);
|
||||
|
||||
typedef enum _MC_VCP_CODE_TYPE {
|
||||
MC_MOMENTARY,
|
||||
MC_SET_PARAMETER
|
||||
} MC_VCP_CODE_TYPE, *LPMC_VCP_CODE_TYPE;
|
||||
|
||||
NTSYSAPI NTSTATUS WINAPI DDCCIGetVCPFeature(
|
||||
_In_ HANDLE hMonitor,
|
||||
_In_ DWORD dwVCPCode,
|
||||
_Out_opt_ LPMC_VCP_CODE_TYPE pvct,
|
||||
_Out_ DWORD *pdwCurrentValue,
|
||||
_Out_opt_ DWORD *pdwMaximumValue
|
||||
);
|
||||
|
||||
NTSYSAPI NTSTATUS WINAPI DestroyPhysicalMonitorInternal(
|
||||
_In_ HANDLE hMonitor
|
||||
);
|
||||
|
||||
NTSTATUS WINAPI GetPhysicalMonitorDescription(
|
||||
_In_ HANDLE hMonitor,
|
||||
_In_ DWORD dwPhysicalMonitorDescriptionSizeInChars,
|
||||
_Out_ LPWSTR szPhysicalMonitorDescription
|
||||
);
|
||||
|
||||
static const char* detectWithWmi(FFlist* result)
|
||||
{
|
||||
@@ -40,33 +69,55 @@ static const char* detectWithWmi(FFlist* result)
|
||||
|
||||
static const char* detectWithDdcci(const FFDisplayServerResult* displayServer, FFlist* result)
|
||||
{
|
||||
FF_LIBRARY_LOAD_MESSAGE(dxva2, "dxva2" FF_LIBRARY_EXTENSION, 1)
|
||||
FF_LIBRARY_LOAD_SYMBOL_MESSAGE(dxva2, GetPhysicalMonitorsFromHMONITOR)
|
||||
FF_LIBRARY_LOAD_SYMBOL_MESSAGE(dxva2, GetMonitorBrightness)
|
||||
FF_LIBRARY_LOAD_SYMBOL_MESSAGE(dxva2, DestroyPhysicalMonitor)
|
||||
void* gdi32 = ffLibraryGetModule(L"gdi32.dll");
|
||||
if (!gdi32) return "ffLibraryGetModule(gdi32.dll) failed";
|
||||
FF_LIBRARY_LOAD_SYMBOL_MESSAGE(gdi32, GetPhysicalMonitors)
|
||||
FF_LIBRARY_LOAD_SYMBOL_MESSAGE(gdi32, DDCCIGetVCPFeature)
|
||||
FF_LIBRARY_LOAD_SYMBOL_MESSAGE(gdi32, DestroyPhysicalMonitorInternal)
|
||||
|
||||
FF_LIST_FOR_EACH(FFDisplayResult, display, displayServer->displays)
|
||||
{
|
||||
PHYSICAL_MONITOR physicalMonitor;
|
||||
if (ffGetPhysicalMonitorsFromHMONITOR((HMONITOR)(uintptr_t) display->id, 1, &physicalMonitor))
|
||||
if (display->type == FF_DISPLAY_TYPE_BUILTIN) continue;
|
||||
|
||||
MONITORINFOEXW mi;
|
||||
mi.cbSize = sizeof(mi);
|
||||
if (!GetMonitorInfoW((HMONITOR)(uintptr_t) display->id, (LPMONITORINFO) &mi))
|
||||
continue;
|
||||
|
||||
UNICODE_STRING deviceName = {
|
||||
.Length = (USHORT) (wcslen(mi.szDevice) * sizeof(wchar_t)),
|
||||
.MaximumLength = 0,
|
||||
.Buffer = mi.szDevice,
|
||||
};
|
||||
HANDLE physicalMonitor;
|
||||
DWORD monitorCount = 0;
|
||||
if (NT_SUCCESS(ffGetPhysicalMonitors(&deviceName, 1, &monitorCount, &physicalMonitor)) && monitorCount >= 1)
|
||||
{
|
||||
DWORD min = 0, curr = 0, max = 0;
|
||||
if (ffGetMonitorBrightness(physicalMonitor.hPhysicalMonitor, &min, &curr, &max))
|
||||
DWORD curr = 0, max = 0;
|
||||
if (NT_SUCCESS(ffDDCCIGetVCPFeature(physicalMonitor, 0x10 /* luminance */, NULL, &curr, &max)))
|
||||
{
|
||||
FFBrightnessResult* brightness = (FFBrightnessResult*) ffListAdd(result);
|
||||
|
||||
if (display->name.length)
|
||||
if (display->name.length > 0)
|
||||
ffStrbufInitCopy(&brightness->name, &display->name);
|
||||
else
|
||||
ffStrbufInitWS(&brightness->name, physicalMonitor.szPhysicalMonitorDescription);
|
||||
|
||||
{
|
||||
FF_LIBRARY_LOAD_SYMBOL_LAZY(gdi32, GetPhysicalMonitorDescription)
|
||||
if (ffGetPhysicalMonitorDescription)
|
||||
{
|
||||
wchar_t description[128 /*MUST be PHYSICAL_MONITOR_DESCRIPTION_SIZE*/];
|
||||
if (NT_SUCCESS(ffGetPhysicalMonitorDescription(physicalMonitor, ARRAY_SIZE(description), description)))
|
||||
ffStrbufInitWS(&brightness->name, description);
|
||||
}
|
||||
if (brightness->name.length == 0)
|
||||
ffStrbufSetNWS(&brightness->name, deviceName.Length / 2, deviceName.Buffer);
|
||||
}
|
||||
brightness->max = max;
|
||||
brightness->min = min;
|
||||
brightness->min = 0;
|
||||
brightness->current = curr;
|
||||
brightness->builtin = false;
|
||||
}
|
||||
|
||||
ffDestroyPhysicalMonitor(physicalMonitor.hPhysicalMonitor);
|
||||
ffDestroyPhysicalMonitorInternal(physicalMonitor);
|
||||
}
|
||||
}
|
||||
return NULL;
|
||||
|
||||
@@ -38,10 +38,8 @@ const char* detectThermalTemp(const FFCPUOptions* options, double* result)
|
||||
|
||||
if (options->tempSensor.length > 0)
|
||||
{
|
||||
int written = MultiByteToWideChar(CP_UTF8, MB_ERR_INVALID_CHARS, options->tempSensor.chars, (int) options->tempSensor.length, querySpec.Name, (int)(ARRAY_SIZE(querySpec.Name) - 1));
|
||||
if (written == 0)
|
||||
if (!NT_SUCCESS(RtlUTF8ToUnicodeN(querySpec.Name, (ULONG) sizeof(querySpec.Name), NULL, options->tempSensor.chars, (ULONG)options->tempSensor.length + 1)))
|
||||
return "Invalid temp sensor string";
|
||||
querySpec.Name[written] = L'\0';
|
||||
}
|
||||
|
||||
DWORD dataSize = 0;
|
||||
@@ -252,28 +250,18 @@ static const char* detectNCores(FFCPUResult* cpu)
|
||||
|
||||
static const char* detectByRegistry(FFCPUResult* cpu)
|
||||
{
|
||||
FF_HKEY_AUTO_DESTROY hKey = NULL;
|
||||
FF_AUTO_CLOSE_FD HANDLE hKey = NULL;
|
||||
if(!ffRegOpenKeyForRead(HKEY_LOCAL_MACHINE, L"HARDWARE\\DESCRIPTION\\System\\CentralProcessor\\0", &hKey, NULL))
|
||||
return "ffRegOpenKeyForRead(HKEY_LOCAL_MACHINE, L\"HARDWARE\\DESCRIPTION\\System\\CentralProcessor\\0\", &hKey, NULL) failed";
|
||||
|
||||
ffRegReadStrbuf(hKey, L"ProcessorNameString", &cpu->name, NULL);
|
||||
if (ffRegReadStrbuf(hKey, L"VendorIdentifier", &cpu->vendor, NULL))
|
||||
if (ffRegReadValues(hKey, 3, (FFRegValueArg[]) {
|
||||
FF_ARG(cpu->name, L"ProcessorNameString"),
|
||||
FF_ARG(cpu->vendor, L"VendorIdentifier"),
|
||||
FF_ARG(cpu->frequencyBase, L"~MHz"),
|
||||
}, NULL))
|
||||
ffStrbufTrimRightSpace(&cpu->vendor);
|
||||
|
||||
if (cpu->coresLogical == 0)
|
||||
{
|
||||
FF_HKEY_AUTO_DESTROY hProcsKey = NULL;
|
||||
if (ffRegOpenKeyForRead(HKEY_LOCAL_MACHINE, L"HARDWARE\\DESCRIPTION\\System\\CentralProcessor", &hProcsKey, NULL))
|
||||
{
|
||||
uint32_t cores;
|
||||
if (ffRegGetNSubKeys(hProcsKey, &cores, NULL))
|
||||
cpu->coresOnline = cpu->coresPhysical = cpu->coresLogical = (uint16_t) cores;
|
||||
}
|
||||
}
|
||||
|
||||
uint32_t mhz;
|
||||
if(ffRegReadUint(hKey, L"~MHz", &mhz, NULL))
|
||||
cpu->frequencyBase = mhz;
|
||||
else
|
||||
return "ffRegReadValues() failed for CPU registry key";
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
@@ -44,12 +44,15 @@ static const char* parseCpuCacheIndex(FFstrbuf* path, FFCPUCacheResult* result,
|
||||
|
||||
ffStrbufSubstrBefore(path, baseLen);
|
||||
ffStrbufAppendS(path, "/shared_cpu_list");
|
||||
if (!ffReadFileBuffer(path->chars, buffer))
|
||||
return "ffReadFileBuffer(\"/sys/devices/system/cpu/cpuX/cache/indexX/shared_cpu_list\") == NULL";
|
||||
ffStrbufClear(buffer);
|
||||
ffStrbufAppendC(buffer, '[');
|
||||
if (!ffAppendFileBuffer(path->chars, buffer))
|
||||
return "ffAppendFileBuffer(\"/sys/devices/system/cpu/cpuX/cache/indexX/shared_cpu_list\") == NULL";
|
||||
ffStrbufTrimRightSpace(buffer);
|
||||
|
||||
// deduplicate shared caches
|
||||
ffStrbufAppendF(buffer, "_%u_%u_%u_%u\n", level, sizeKb, lineSize, cacheType);
|
||||
ffStrbufAppendF(buffer, "_%u_%u_%u_%u]", level, sizeKb, lineSize, cacheType);
|
||||
|
||||
if (ffStrbufContain(added, buffer)) return NULL;
|
||||
ffStrbufAppend(added, buffer);
|
||||
ffCPUCacheAddItem(result, level, sizeKb * 1024, lineSize, cacheType);
|
||||
|
||||
@@ -37,9 +37,12 @@ const char* ffDetectCPUCache(FFCPUCacheResult* result)
|
||||
if (!data)
|
||||
return "Cache information is not found in SMBIOS data";
|
||||
|
||||
for (; data->Header.Type == FF_SMBIOS_TYPE_CACHE_INFO;
|
||||
data = (const FFSmbiosCacheInfo*) ffSmbiosNextEntry(&data->Header))
|
||||
const FFSmbiosCacheInfo* endOfTable = (const FFSmbiosCacheInfo*) (*smbiosTable)[FF_SMBIOS_TYPE_END_OF_TABLE];
|
||||
for (; data != endOfTable; data = (const FFSmbiosCacheInfo*) ffSmbiosNextEntry(&data->Header))
|
||||
{
|
||||
if (data->Header.Type != FF_SMBIOS_TYPE_CACHE_INFO)
|
||||
continue;
|
||||
|
||||
bool enabled = !!(data->CacheConfiguration & (1 << 7));
|
||||
if (!enabled)
|
||||
continue;
|
||||
|
||||
@@ -1,17 +1,18 @@
|
||||
#include "cursor.h"
|
||||
|
||||
#include "common/io.h"
|
||||
#include "common/windows/registry.h"
|
||||
|
||||
void ffDetectCursor(FFCursorResult* result)
|
||||
{
|
||||
FF_HKEY_AUTO_DESTROY hKey;
|
||||
FF_AUTO_CLOSE_FD HANDLE hKey = NULL;
|
||||
if(ffRegOpenKeyForRead(HKEY_CURRENT_USER, L"Control Panel\\Cursors", &hKey, &result->error))
|
||||
{
|
||||
if (!ffRegReadStrbuf(hKey, NULL, &result->theme, &result->error))
|
||||
return;
|
||||
|
||||
uint32_t cursorBaseSize;
|
||||
if (ffRegReadUint(hKey, L"CursorBaseSize", &cursorBaseSize, NULL))
|
||||
ffStrbufAppendF(&result->size, "%u", (unsigned) cursorBaseSize);
|
||||
if (ffRegReadValues(hKey, 2, (FFRegValueArg[]) {
|
||||
FF_ARG(result->theme, NULL),
|
||||
FF_ARG(cursorBaseSize, L"CursorBaseSize"),
|
||||
}, &result->error))
|
||||
ffStrbufAppendUInt(&result->size, cursorBaseSize);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -140,21 +140,7 @@ static void detectName(FFDisk* disk)
|
||||
|
||||
if (disk->name.length == 0) return;
|
||||
|
||||
// Basic\x20data\x20partition
|
||||
for (uint32_t i = ffStrbufFirstIndexS(&disk->name, "\\x");
|
||||
i != disk->name.length;
|
||||
i = ffStrbufNextIndexS(&disk->name, i + 1, "\\x"))
|
||||
{
|
||||
uint32_t len = (uint32_t) strlen("\\x20");
|
||||
if (disk->name.length >= len)
|
||||
{
|
||||
char bak = disk->name.chars[i + len];
|
||||
disk->name.chars[i + len] = '\0';
|
||||
disk->name.chars[i] = (char) strtoul(&disk->name.chars[i + 2], NULL, 16);
|
||||
ffStrbufRemoveSubstr(&disk->name, i + 1, i + len);
|
||||
disk->name.chars[i + 1] = bak;
|
||||
}
|
||||
}
|
||||
ffStrbufDecodeHexEscapeSequences(&disk->name);
|
||||
}
|
||||
|
||||
#ifdef __ANDROID__
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
#include "disk.h"
|
||||
#include "common/io.h"
|
||||
#include "common/time.h"
|
||||
#include "common/windows/unicode.h"
|
||||
#include "common/windows/nt.h"
|
||||
|
||||
@@ -101,7 +102,7 @@ const char* ffDetectDisksImpl(FFDiskOptions* options, FFlist* disks)
|
||||
if (fsVolume->VolumeLabelLength > 0)
|
||||
ffStrbufSetNWS(&disk->name, fsVolume->VolumeLabelLength / sizeof(WCHAR), fsVolume->VolumeLabel);
|
||||
if (fsVolume->VolumeCreationTime.QuadPart)
|
||||
disk->createTime = ((uint64_t) fsVolume->VolumeCreationTime.QuadPart - 116444736000000000ull) / 10000ull;
|
||||
disk->createTime = ffFileTimeToUnixMs((uint64_t) fsVolume->VolumeCreationTime.QuadPart);
|
||||
}
|
||||
|
||||
if (fsAttr)
|
||||
|
||||
@@ -5,8 +5,7 @@ FFDisplayResult* ffdsAppendDisplay(
|
||||
uint32_t width,
|
||||
uint32_t height,
|
||||
double refreshRate,
|
||||
uint32_t scaledWidth,
|
||||
uint32_t scaledHeight,
|
||||
uint32_t dpi,
|
||||
uint32_t preferredWidth,
|
||||
uint32_t preferredHeight,
|
||||
double preferredRefreshRate,
|
||||
@@ -26,8 +25,7 @@ FFDisplayResult* ffdsAppendDisplay(
|
||||
display->width = width;
|
||||
display->height = height;
|
||||
display->refreshRate = refreshRate;
|
||||
display->scaledWidth = scaledWidth;
|
||||
display->scaledHeight = scaledHeight;
|
||||
display->dpi = dpi ?: 96; // 0 means unknown
|
||||
display->preferredWidth = preferredWidth;
|
||||
display->preferredHeight = preferredHeight;
|
||||
display->preferredRefreshRate = preferredRefreshRate;
|
||||
|
||||
@@ -72,8 +72,7 @@ typedef struct FFDisplayResult
|
||||
uint32_t width; // in px
|
||||
uint32_t height; // in px
|
||||
double refreshRate; // in Hz
|
||||
uint32_t scaledWidth; // in px
|
||||
uint32_t scaledHeight; // in px
|
||||
uint32_t dpi; // Base 96
|
||||
uint32_t preferredWidth; // in px
|
||||
uint32_t preferredHeight; // in px
|
||||
double preferredRefreshRate; // in Hz
|
||||
@@ -110,8 +109,7 @@ FFDisplayResult* ffdsAppendDisplay(
|
||||
uint32_t width,
|
||||
uint32_t height,
|
||||
double refreshRate,
|
||||
uint32_t scaledWidth,
|
||||
uint32_t scaledHeight,
|
||||
uint32_t dpi,
|
||||
uint32_t preferredWidth,
|
||||
uint32_t preferredHeight,
|
||||
double preferredRefreshRate,
|
||||
|
||||
@@ -85,7 +85,7 @@ static void detectWithDumpsys(FFDisplayServerResult* ds)
|
||||
FFDisplayResult* display = ffdsAppendDisplay(ds,
|
||||
(uint32_t)width, (uint32_t)height,
|
||||
refreshRate,
|
||||
0, 0,
|
||||
0,
|
||||
0, 0,
|
||||
0,
|
||||
0,
|
||||
@@ -117,11 +117,11 @@ static bool detectWithGetprop(FFDisplayServerResult* ds)
|
||||
ffStrbufSubstrAfterFirstC(&buffer, ',');
|
||||
uint32_t height = (uint32_t) ffStrbufToUInt(&buffer, 0);
|
||||
ffStrbufSubstrAfterFirstC(&buffer, ',');
|
||||
double scaleFactor = (double) ffStrbufToUInt(&buffer, 0) / 160.;
|
||||
uint32_t dpi = (uint32_t) ffStrbufToUInt(&buffer, 0) * 96 / 160;
|
||||
FFDisplayResult* display = ffdsAppendDisplay(ds,
|
||||
width, height,
|
||||
0,
|
||||
(uint32_t) (width / scaleFactor + .5), (uint32_t) (height / scaleFactor + .5),
|
||||
dpi,
|
||||
0, 0,
|
||||
0,
|
||||
0,
|
||||
|
||||
@@ -116,12 +116,14 @@ static void detectDisplays(FFDisplayServerResult* ds)
|
||||
physicalHeight = (uint32_t) (size.height + 0.5);
|
||||
}
|
||||
|
||||
uint32_t pixelWidth = (uint32_t) CGDisplayModeGetPixelWidth(mode);
|
||||
uint32_t pixelHeight = (uint32_t) CGDisplayModeGetPixelHeight(mode);
|
||||
|
||||
FFDisplayResult* display = ffdsAppendDisplay(ds,
|
||||
(uint32_t)CGDisplayModeGetPixelWidth(mode),
|
||||
(uint32_t)CGDisplayModeGetPixelHeight(mode),
|
||||
pixelWidth,
|
||||
pixelHeight,
|
||||
refreshRate,
|
||||
(uint32_t)CGDisplayModeGetWidth(mode),
|
||||
(uint32_t)CGDisplayModeGetHeight(mode),
|
||||
pixelHeight * 96 / (uint32_t)CGDisplayModeGetHeight(mode),
|
||||
preferredWidth,
|
||||
preferredHeight,
|
||||
preferredRefreshRate,
|
||||
|
||||
@@ -37,13 +37,11 @@ static void detectDisplays(FFDisplayServerResult* ds)
|
||||
|
||||
uint32_t width = (uint32_t) s.Frame().Width() + 1;
|
||||
uint32_t height = (uint32_t) (uint32_t)s.Frame().Height() + 1;
|
||||
double scaleFactor = (double) 1.0;
|
||||
FFDisplayResult* res = ffdsAppendDisplay(ds,
|
||||
width,
|
||||
height,
|
||||
(double)mode.timing.pixel_clock * 1000 / (mode.timing.v_total * mode.timing.h_total),
|
||||
(uint32_t) (width / scaleFactor + .5),
|
||||
(uint32_t) (height / scaleFactor + .5),
|
||||
0,
|
||||
0,
|
||||
0,
|
||||
0,
|
||||
|
||||
@@ -1,33 +1,26 @@
|
||||
#include "displayserver.h"
|
||||
#include "common/windows/unicode.h"
|
||||
#include "common/edidHelper.h"
|
||||
#include "common/windows/registry.h"
|
||||
#include "common/windows/unicode.h"
|
||||
|
||||
#include <windows.h>
|
||||
#include <shellscalingapi.h>
|
||||
|
||||
static inline void freeArgBuffer(FFArgBuffer* buffer)
|
||||
{
|
||||
if (buffer->data)
|
||||
free(buffer->data);
|
||||
buffer->data = NULL;
|
||||
buffer->length = 0;
|
||||
}
|
||||
#define FF_AUTO_FREE_ARG_BUFFER __attribute__((__cleanup__(freeArgBuffer)))
|
||||
|
||||
// http://undoc.airesoft.co.uk/user32.dll/IsThreadDesktopComposited.php
|
||||
BOOL WINAPI IsThreadDesktopComposited();
|
||||
BOOL WINAPI GetDpiForMonitorInternal(HMONITOR hmonitor, MONITOR_DPI_TYPE dpiType, UINT* dpiX, UINT* dpiY);
|
||||
|
||||
static void detectDisplays(FFDisplayServerResult* ds)
|
||||
{
|
||||
#if FF_WIN7_COMPAT
|
||||
static __typeof__(GetDpiForMonitorInternal)* ffGetDpiForMonitor;
|
||||
if (!ffGetDpiForMonitor)
|
||||
{
|
||||
HMODULE user32 = GetModuleHandleW(L"user32.dll");
|
||||
if (user32)
|
||||
{
|
||||
// GetDpiForMonitorInternal (returns BOOL) is in user32, while GetDpiForMonitor (returns HRESULT) is in shcore.
|
||||
// Both are available since Windows 8.1. Not sure why Microsoft decided to put them in different DLLs, but whatever.
|
||||
// Use GetDpiForMonitorInternal for loading one less dll
|
||||
ffGetDpiForMonitor = (void*) GetProcAddress(user32, "GetDpiForMonitorInternal");
|
||||
}
|
||||
}
|
||||
#else
|
||||
#define ffGetDpiForMonitor GetDpiForMonitorInternal
|
||||
#endif
|
||||
|
||||
DISPLAYCONFIG_PATH_INFO paths[128];
|
||||
uint32_t pathCount = ARRAY_SIZE(paths);
|
||||
DISPLAYCONFIG_MODE_INFO modes[256];
|
||||
@@ -46,15 +39,6 @@ static void detectDisplays(FFDisplayServerResult* ds)
|
||||
const DISPLAYCONFIG_PATH_INFO* path = &paths[i];
|
||||
const DISPLAYCONFIG_SOURCE_MODE* sourceMode = &modes[path->sourceInfo.modeInfoIdx].sourceMode;
|
||||
|
||||
DISPLAYCONFIG_SOURCE_DEVICE_NAME sourceName = {
|
||||
.header = {
|
||||
.type = DISPLAYCONFIG_DEVICE_INFO_GET_SOURCE_NAME,
|
||||
.size = sizeof(sourceName),
|
||||
.adapterId = path->sourceInfo.adapterId,
|
||||
.id = path->sourceInfo.id,
|
||||
},
|
||||
};
|
||||
|
||||
FF_STRBUF_AUTO_DESTROY name = ffStrbufCreate();
|
||||
uint32_t physicalWidth = 0, physicalHeight = 0;
|
||||
|
||||
@@ -66,9 +50,7 @@ static void detectDisplays(FFDisplayServerResult* ds)
|
||||
.id = path->targetInfo.id,
|
||||
},
|
||||
};
|
||||
uint8_t edidData[1024];
|
||||
DWORD edidLength = 0;
|
||||
|
||||
FF_AUTO_FREE_ARG_BUFFER FFArgBuffer edid = {};
|
||||
if(DisplayConfigGetDeviceInfo(&targetName.header) == ERROR_SUCCESS)
|
||||
{
|
||||
wchar_t regPath[256] = L"SYSTEM\\CurrentControlSet\\Enum";
|
||||
@@ -86,16 +68,17 @@ static void detectDisplays(FFDisplayServerResult* ds)
|
||||
}
|
||||
wcscpy(pRegPath, L"Device Parameters");
|
||||
|
||||
edidLength = ARRAY_SIZE(edidData);
|
||||
if (RegGetValueW(HKEY_LOCAL_MACHINE, regPath, L"EDID", RRF_RT_REG_BINARY, NULL, edidData, &edidLength) == ERROR_SUCCESS &&
|
||||
edidLength > 0 && edidLength % 128 == 0)
|
||||
FF_AUTO_CLOSE_FD HANDLE hKey = NULL;
|
||||
if (ffRegOpenKeyForRead(HKEY_LOCAL_MACHINE, regPath, &hKey, NULL) &&
|
||||
ffRegReadData(hKey, L"EDID", &edid, NULL) &&
|
||||
ffEdidIsValid(edid.data, edid.length))
|
||||
{
|
||||
ffEdidGetName(edidData, &name);
|
||||
ffEdidGetPhysicalSize(edidData, &physicalWidth, &physicalHeight);
|
||||
ffEdidGetName(edid.data, &name);
|
||||
ffEdidGetPhysicalSize(edid.data, &physicalWidth, &physicalHeight);
|
||||
}
|
||||
else
|
||||
{
|
||||
edidLength = 0;
|
||||
edid.length = 0;
|
||||
if (targetName.flags.friendlyNameFromEdid)
|
||||
ffStrbufSetWS(&name, targetName.monitorFriendlyDeviceName);
|
||||
else
|
||||
@@ -109,17 +92,6 @@ static void detectDisplays(FFDisplayServerResult* ds)
|
||||
|
||||
uint32_t width = sourceMode->width;
|
||||
uint32_t height = sourceMode->height;
|
||||
if (path->targetInfo.rotation == DISPLAYCONFIG_ROTATION_ROTATE90 ||
|
||||
path->targetInfo.rotation == DISPLAYCONFIG_ROTATION_ROTATE270)
|
||||
{
|
||||
uint32_t temp = width;
|
||||
width = height;
|
||||
height = temp;
|
||||
temp = physicalWidth;
|
||||
physicalWidth = physicalHeight;
|
||||
physicalHeight = temp;
|
||||
}
|
||||
|
||||
uint32_t rotation;
|
||||
switch (path->targetInfo.rotation)
|
||||
{
|
||||
@@ -145,15 +117,11 @@ static void detectDisplays(FFDisplayServerResult* ds)
|
||||
}
|
||||
|
||||
uint32_t systemDpi = 0;
|
||||
|
||||
if (ffGetDpiForMonitor)
|
||||
HMONITOR hMonitor = MonitorFromPoint(*(POINT*)&sourceMode->position, MONITOR_DEFAULTTONULL);
|
||||
if (hMonitor)
|
||||
{
|
||||
HMONITOR hMonitor = MonitorFromPoint(*(POINT*)&sourceMode->position, MONITOR_DEFAULTTONULL);
|
||||
if (hMonitor)
|
||||
{
|
||||
UINT ignored;
|
||||
ffGetDpiForMonitor(hMonitor, MDT_EFFECTIVE_DPI, &systemDpi, &ignored);
|
||||
}
|
||||
UINT ignored;
|
||||
GetDpiForMonitorInternal(hMonitor, MDT_EFFECTIVE_DPI, &systemDpi, &ignored);
|
||||
}
|
||||
|
||||
if (systemDpi == 0)
|
||||
@@ -164,12 +132,19 @@ static void detectDisplays(FFDisplayServerResult* ds)
|
||||
ReleaseDC(NULL, hdc);
|
||||
}
|
||||
|
||||
if (path->targetInfo.rotation == DISPLAYCONFIG_ROTATION_ROTATE90 ||
|
||||
path->targetInfo.rotation == DISPLAYCONFIG_ROTATION_ROTATE270)
|
||||
{
|
||||
uint32_t temp = width;
|
||||
width = height;
|
||||
height = temp;
|
||||
}
|
||||
|
||||
FFDisplayResult* display = ffdsAppendDisplay(ds,
|
||||
width,
|
||||
height,
|
||||
path->targetInfo.refreshRate.Numerator / (double) path->targetInfo.refreshRate.Denominator,
|
||||
width * 96 / systemDpi,
|
||||
height * 96 / systemDpi,
|
||||
systemDpi,
|
||||
preferredMode.width,
|
||||
preferredMode.height,
|
||||
preferredRefreshRate,
|
||||
@@ -181,7 +156,7 @@ static void detectDisplays(FFDisplayServerResult* ds)
|
||||
path->targetInfo.outputTechnology == DISPLAYCONFIG_OUTPUT_TECHNOLOGY_UDI_EMBEDDED
|
||||
? FF_DISPLAY_TYPE_BUILTIN : FF_DISPLAY_TYPE_EXTERNAL,
|
||||
sourceMode->position.x == 0 && sourceMode->position.y == 0,
|
||||
path->sourceInfo.id,
|
||||
(uintptr_t) hMonitor,
|
||||
physicalWidth,
|
||||
physicalHeight,
|
||||
"GDI"
|
||||
@@ -230,8 +205,8 @@ static void detectDisplays(FFDisplayServerResult* ds)
|
||||
else
|
||||
display->hdrStatus = FF_DISPLAY_HDR_STATUS_UNKNOWN;
|
||||
}
|
||||
if (edidLength > 0)
|
||||
ffEdidGetSerialAndManufactureDate(edidData, &display->serial, &display->manufactureYear, &display->manufactureWeek);
|
||||
if (edid.length > 0)
|
||||
ffEdidGetSerialAndManufactureDate(edid.data, &display->serial, &display->manufactureYear, &display->manufactureWeek);
|
||||
display->drrStatus = path->flags & DISPLAYCONFIG_PATH_BOOST_REFRESH_RATE ? FF_DISPLAY_DRR_STATUS_ENABLED : FF_DISPLAY_DRR_STATUS_DISABLED;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -79,7 +79,7 @@ void ffConnectDisplayServerImpl(FFDisplayServerResult* ds)
|
||||
if (ffSettingsGetFreeBSDKenv("screen.height", &buf))
|
||||
{
|
||||
uint32_t height = (uint32_t) ffStrbufToUInt(&buf, 0);
|
||||
ffdsAppendDisplay(ds, width, height, 0, 0, 0, 0, 0, 0, 0, NULL, FF_DISPLAY_TYPE_UNKNOWN, false, 0, 0, 0, "kenv");
|
||||
ffdsAppendDisplay(ds, width, height, 0, 0, 0, 0, 0, 0, NULL, FF_DISPLAY_TYPE_UNKNOWN, false, 0, 0, 0, "kenv");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -83,7 +83,7 @@ static const char* drmParseSysfs(FFDisplayServerResult* result)
|
||||
result,
|
||||
width, height,
|
||||
refreshRate,
|
||||
0, 0,
|
||||
0,
|
||||
0, 0,
|
||||
0,
|
||||
0,
|
||||
@@ -398,7 +398,7 @@ static const char* drmConnectLibdrm(FFDisplayServerResult* result)
|
||||
FFDisplayResult* item = ffdsAppendDisplay(result,
|
||||
width, height,
|
||||
refreshRate,
|
||||
0, 0,
|
||||
0,
|
||||
preferredWidth, preferredHeight,
|
||||
preferredRefreshRate,
|
||||
0,
|
||||
|
||||
@@ -25,7 +25,7 @@ static void waylandOutputModeListener(void* data, FF_MAYBE_UNUSED struct wl_outp
|
||||
static void waylandOutputScaleListener(void* data, FF_MAYBE_UNUSED struct wl_output* output, int32_t scale)
|
||||
{
|
||||
WaylandDisplay* display = data;
|
||||
display->scale = scale;
|
||||
display->dpi = 96 * (uint32_t) scale;
|
||||
}
|
||||
|
||||
static void waylandOutputGeometryListener(void *data,
|
||||
@@ -51,7 +51,7 @@ static void handleXdgLogicalSize(void *data, FF_MAYBE_UNUSED struct zxdg_output_
|
||||
// Seems the values are only useful when ractional scale is enabled
|
||||
if (width < display->width)
|
||||
{
|
||||
display->scale = (double) display->width / width;
|
||||
display->dpi = (uint32_t) (display->width * 96 / width);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -86,7 +86,6 @@ const char* ffWaylandHandleGlobalOutput(WaylandData* wldata, struct wl_registry*
|
||||
|
||||
WaylandDisplay display = {
|
||||
.parent = wldata,
|
||||
.scale = 1,
|
||||
.transform = WL_OUTPUT_TRANSFORM_NORMAL,
|
||||
.type = FF_DISPLAY_TYPE_UNKNOWN,
|
||||
.name = ffStrbufCreate(),
|
||||
@@ -128,8 +127,7 @@ const char* ffWaylandHandleGlobalOutput(WaylandData* wldata, struct wl_registry*
|
||||
(uint32_t) display.width,
|
||||
(uint32_t) display.height,
|
||||
display.refreshRate / 1000.0,
|
||||
(uint32_t) (display.width / display.scale + .5),
|
||||
(uint32_t) (display.height / display.scale + .5),
|
||||
display.dpi,
|
||||
(uint32_t) display.preferredWidth,
|
||||
(uint32_t) display.preferredHeight,
|
||||
display.preferredRefreshRate / 1000.0,
|
||||
|
||||
@@ -14,6 +14,7 @@ extern "C" {
|
||||
/**
|
||||
* @page page_kde_output_device_v2 The kde_output_device_v2 protocol
|
||||
* @section page_ifaces_kde_output_device_v2 Interfaces
|
||||
* - @subpage page_iface_kde_output_device_registry_v2 - output devices
|
||||
* - @subpage page_iface_kde_output_device_v2 - output configuration representation
|
||||
* - @subpage page_iface_kde_output_device_mode_v2 - output mode
|
||||
* @section page_copyright_kde_output_device_v2 Copyright
|
||||
@@ -29,8 +30,32 @@ extern "C" {
|
||||
* </pre>
|
||||
*/
|
||||
struct kde_output_device_mode_v2;
|
||||
struct kde_output_device_registry_v2;
|
||||
struct kde_output_device_v2;
|
||||
|
||||
#ifndef KDE_OUTPUT_DEVICE_REGISTRY_V2_INTERFACE
|
||||
#define KDE_OUTPUT_DEVICE_REGISTRY_V2_INTERFACE
|
||||
/**
|
||||
* @page page_iface_kde_output_device_registry_v2 kde_output_device_registry_v2
|
||||
* @section page_iface_kde_output_device_registry_v2_desc Description
|
||||
*
|
||||
* This interface can be used to list output devices.
|
||||
*
|
||||
* If this global is bound with a version less than 21, the unsupported_version
|
||||
* protocol error will be posted.
|
||||
* @section page_iface_kde_output_device_registry_v2_api API
|
||||
* See @ref iface_kde_output_device_registry_v2.
|
||||
*/
|
||||
/**
|
||||
* @defgroup iface_kde_output_device_registry_v2 The kde_output_device_registry_v2 interface
|
||||
*
|
||||
* This interface can be used to list output devices.
|
||||
*
|
||||
* If this global is bound with a version less than 21, the unsupported_version
|
||||
* protocol error will be posted.
|
||||
*/
|
||||
extern const struct wl_interface kde_output_device_registry_v2_interface;
|
||||
#endif
|
||||
#ifndef KDE_OUTPUT_DEVICE_V2_INTERFACE
|
||||
#define KDE_OUTPUT_DEVICE_V2_INTERFACE
|
||||
/**
|
||||
@@ -120,6 +145,119 @@ extern const struct wl_interface kde_output_device_v2_interface;
|
||||
extern const struct wl_interface kde_output_device_mode_v2_interface;
|
||||
#endif
|
||||
|
||||
#ifndef KDE_OUTPUT_DEVICE_REGISTRY_V2_ERROR_ENUM
|
||||
#define KDE_OUTPUT_DEVICE_REGISTRY_V2_ERROR_ENUM
|
||||
/**
|
||||
* @ingroup iface_kde_output_device_registry_v2
|
||||
* kde_output_device_registry_v2 error values
|
||||
*
|
||||
* These errors can be emitted in response to some requests.
|
||||
*/
|
||||
enum kde_output_device_registry_v2_error {
|
||||
/**
|
||||
* the registry was bound with an unsupported version
|
||||
*/
|
||||
KDE_OUTPUT_DEVICE_REGISTRY_V2_ERROR_UNSUPPORTED_VERSION = 0,
|
||||
};
|
||||
#endif /* KDE_OUTPUT_DEVICE_REGISTRY_V2_ERROR_ENUM */
|
||||
|
||||
/**
|
||||
* @ingroup iface_kde_output_device_registry_v2
|
||||
* @struct kde_output_device_registry_v2_listener
|
||||
*/
|
||||
struct kde_output_device_registry_v2_listener {
|
||||
/**
|
||||
* no new output announcements
|
||||
*
|
||||
* This event is sent in response to the stop request. The
|
||||
* compositor will immediately destroy the object after sending
|
||||
* this event.
|
||||
* @since 21
|
||||
*/
|
||||
void (*finished)(void *data,
|
||||
struct kde_output_device_registry_v2 *kde_output_device_registry_v2);
|
||||
/**
|
||||
* new available output
|
||||
*
|
||||
* This event is sent when a new output is connected or after
|
||||
* binding this global to list all available outputs.
|
||||
* @since 21
|
||||
*/
|
||||
void (*output)(void *data,
|
||||
struct kde_output_device_registry_v2 *kde_output_device_registry_v2,
|
||||
struct kde_output_device_v2 *output);
|
||||
};
|
||||
|
||||
/**
|
||||
* @ingroup iface_kde_output_device_registry_v2
|
||||
*/
|
||||
static inline int
|
||||
kde_output_device_registry_v2_add_listener(struct kde_output_device_registry_v2 *kde_output_device_registry_v2,
|
||||
const struct kde_output_device_registry_v2_listener *listener, void *data)
|
||||
{
|
||||
return wl_proxy_add_listener((struct wl_proxy *) kde_output_device_registry_v2,
|
||||
(void (**)(void)) listener, data);
|
||||
}
|
||||
|
||||
#define KDE_OUTPUT_DEVICE_REGISTRY_V2_STOP 0
|
||||
|
||||
/**
|
||||
* @ingroup iface_kde_output_device_registry_v2
|
||||
*/
|
||||
#define KDE_OUTPUT_DEVICE_REGISTRY_V2_FINISHED_SINCE_VERSION 21
|
||||
/**
|
||||
* @ingroup iface_kde_output_device_registry_v2
|
||||
*/
|
||||
#define KDE_OUTPUT_DEVICE_REGISTRY_V2_OUTPUT_SINCE_VERSION 21
|
||||
|
||||
/**
|
||||
* @ingroup iface_kde_output_device_registry_v2
|
||||
*/
|
||||
#define KDE_OUTPUT_DEVICE_REGISTRY_V2_STOP_SINCE_VERSION 21
|
||||
|
||||
/** @ingroup iface_kde_output_device_registry_v2 */
|
||||
static inline void
|
||||
kde_output_device_registry_v2_set_user_data(struct kde_output_device_registry_v2 *kde_output_device_registry_v2, void *user_data)
|
||||
{
|
||||
wl_proxy_set_user_data((struct wl_proxy *) kde_output_device_registry_v2, user_data);
|
||||
}
|
||||
|
||||
/** @ingroup iface_kde_output_device_registry_v2 */
|
||||
static inline void *
|
||||
kde_output_device_registry_v2_get_user_data(struct kde_output_device_registry_v2 *kde_output_device_registry_v2)
|
||||
{
|
||||
return wl_proxy_get_user_data((struct wl_proxy *) kde_output_device_registry_v2);
|
||||
}
|
||||
|
||||
static inline uint32_t
|
||||
kde_output_device_registry_v2_get_version(struct kde_output_device_registry_v2 *kde_output_device_registry_v2)
|
||||
{
|
||||
return wl_proxy_get_version((struct wl_proxy *) kde_output_device_registry_v2);
|
||||
}
|
||||
|
||||
/** @ingroup iface_kde_output_device_registry_v2 */
|
||||
static inline void
|
||||
kde_output_device_registry_v2_destroy(struct kde_output_device_registry_v2 *kde_output_device_registry_v2)
|
||||
{
|
||||
wl_proxy_destroy((struct wl_proxy *) kde_output_device_registry_v2);
|
||||
}
|
||||
|
||||
/**
|
||||
* @ingroup iface_kde_output_device_registry_v2
|
||||
*
|
||||
* This request indicates that the client no longer wants to receive new
|
||||
* output announcements. The compositor will send the
|
||||
* kde_output_device_registry_v2.finished event in response to this request.
|
||||
* The compositor may still send new output announcements after calling this
|
||||
* request until the kde_output_device_registry_v2.finished event is sent.
|
||||
*/
|
||||
// static inline void
|
||||
// kde_output_device_registry_v2_stop(struct kde_output_device_registry_v2 *kde_output_device_registry_v2)
|
||||
// {
|
||||
// wl_proxy_marshal_flags((struct wl_proxy *) kde_output_device_registry_v2,
|
||||
// KDE_OUTPUT_DEVICE_REGISTRY_V2_STOP, NULL, wl_proxy_get_version((struct wl_proxy *) kde_output_device_registry_v2), 0);
|
||||
// }
|
||||
|
||||
#ifndef KDE_OUTPUT_DEVICE_V2_SUBPIXEL_ENUM
|
||||
#define KDE_OUTPUT_DEVICE_V2_SUBPIXEL_ENUM
|
||||
/**
|
||||
@@ -838,6 +976,16 @@ struct kde_output_device_v2_listener {
|
||||
void (*auto_brightness)(void *data,
|
||||
struct kde_output_device_v2 *kde_output_device_v2,
|
||||
uint32_t enabled);
|
||||
/**
|
||||
* the output has been removed
|
||||
*
|
||||
* This event is sent when the output device is disconnected and
|
||||
* no new updates will be sent. The client should call the
|
||||
* kde_output_device_v2.release request after receiving this event.
|
||||
* @since 21
|
||||
*/
|
||||
void (*removed)(void *data,
|
||||
struct kde_output_device_v2 *kde_output_device_v2);
|
||||
};
|
||||
|
||||
/**
|
||||
@@ -851,6 +999,8 @@ kde_output_device_v2_add_listener(struct kde_output_device_v2 *kde_output_device
|
||||
(void (**)(void)) listener, data);
|
||||
}
|
||||
|
||||
#define KDE_OUTPUT_DEVICE_V2_RELEASE 0
|
||||
|
||||
/**
|
||||
* @ingroup iface_kde_output_device_v2
|
||||
*/
|
||||
@@ -995,7 +1145,15 @@ kde_output_device_v2_add_listener(struct kde_output_device_v2 *kde_output_device
|
||||
* @ingroup iface_kde_output_device_v2
|
||||
*/
|
||||
#define KDE_OUTPUT_DEVICE_V2_AUTO_BRIGHTNESS_SINCE_VERSION 20
|
||||
/**
|
||||
* @ingroup iface_kde_output_device_v2
|
||||
*/
|
||||
#define KDE_OUTPUT_DEVICE_V2_REMOVED_SINCE_VERSION 21
|
||||
|
||||
/**
|
||||
* @ingroup iface_kde_output_device_v2
|
||||
*/
|
||||
#define KDE_OUTPUT_DEVICE_V2_RELEASE_SINCE_VERSION 21
|
||||
|
||||
/** @ingroup iface_kde_output_device_v2 */
|
||||
static inline void
|
||||
@@ -1024,6 +1182,19 @@ kde_output_device_v2_destroy(struct kde_output_device_v2 *kde_output_device_v2)
|
||||
wl_proxy_destroy((struct wl_proxy *) kde_output_device_v2);
|
||||
}
|
||||
|
||||
/**
|
||||
* @ingroup iface_kde_output_device_v2
|
||||
*
|
||||
* This notifies the compositor that the client no longer wishes to use
|
||||
* the kde_output_device_v2 object.
|
||||
*/
|
||||
// static inline void
|
||||
// kde_output_device_v2_release(struct kde_output_device_v2 *kde_output_device_v2)
|
||||
// {
|
||||
// wl_proxy_marshal_flags((struct wl_proxy *) kde_output_device_v2,
|
||||
// KDE_OUTPUT_DEVICE_V2_RELEASE, NULL, wl_proxy_get_version((struct wl_proxy *) kde_output_device_v2), WL_MARSHAL_FLAG_DESTROY);
|
||||
// }
|
||||
|
||||
#ifndef KDE_OUTPUT_DEVICE_MODE_V2_FLAGS_ENUM
|
||||
#define KDE_OUTPUT_DEVICE_MODE_V2_FLAGS_ENUM
|
||||
/**
|
||||
|
||||
@@ -18,6 +18,7 @@
|
||||
#include <wayland-util.h>
|
||||
|
||||
extern const struct wl_interface kde_output_device_mode_v2_interface;
|
||||
extern const struct wl_interface kde_output_device_v2_interface;
|
||||
|
||||
static const struct wl_interface *kde_output_device_v2_types[] = {
|
||||
NULL,
|
||||
@@ -28,14 +29,34 @@ static const struct wl_interface *kde_output_device_v2_types[] = {
|
||||
NULL,
|
||||
NULL,
|
||||
NULL,
|
||||
&kde_output_device_v2_interface,
|
||||
&kde_output_device_mode_v2_interface,
|
||||
&kde_output_device_mode_v2_interface,
|
||||
};
|
||||
|
||||
static const struct wl_message kde_output_device_registry_v2_requests[] = {
|
||||
{ "stop", "21", kde_output_device_v2_types + 0 },
|
||||
};
|
||||
|
||||
static const struct wl_message kde_output_device_registry_v2_events[] = {
|
||||
{ "finished", "21", kde_output_device_v2_types + 0 },
|
||||
{ "output", "21n", kde_output_device_v2_types + 8 },
|
||||
};
|
||||
|
||||
WL_EXPORT const struct wl_interface kde_output_device_registry_v2_interface = {
|
||||
"kde_output_device_registry_v2", 21,
|
||||
1, kde_output_device_registry_v2_requests,
|
||||
2, kde_output_device_registry_v2_events,
|
||||
};
|
||||
|
||||
static const struct wl_message kde_output_device_v2_requests[] = {
|
||||
{ "release", "21", kde_output_device_v2_types + 0 },
|
||||
};
|
||||
|
||||
static const struct wl_message kde_output_device_v2_events[] = {
|
||||
{ "geometry", "iiiiissi", kde_output_device_v2_types + 0 },
|
||||
{ "current_mode", "o", kde_output_device_v2_types + 8 },
|
||||
{ "mode", "n", kde_output_device_v2_types + 9 },
|
||||
{ "current_mode", "o", kde_output_device_v2_types + 9 },
|
||||
{ "mode", "n", kde_output_device_v2_types + 10 },
|
||||
{ "done", "", kde_output_device_v2_types + 0 },
|
||||
{ "scale", "f", kde_output_device_v2_types + 0 },
|
||||
{ "edid", "s", kde_output_device_v2_types + 0 },
|
||||
@@ -69,12 +90,13 @@ static const struct wl_message kde_output_device_v2_events[] = {
|
||||
{ "sharpness", "17u", kde_output_device_v2_types + 0 },
|
||||
{ "priority", "18u", kde_output_device_v2_types + 0 },
|
||||
{ "auto_brightness", "20u", kde_output_device_v2_types + 0 },
|
||||
{ "removed", "21", kde_output_device_v2_types + 0 },
|
||||
};
|
||||
|
||||
WL_EXPORT const struct wl_interface kde_output_device_v2_interface = {
|
||||
"kde_output_device_v2", 20,
|
||||
0, NULL,
|
||||
36, kde_output_device_v2_events,
|
||||
"kde_output_device_v2", 21,
|
||||
1, kde_output_device_v2_requests,
|
||||
37, kde_output_device_v2_events,
|
||||
};
|
||||
|
||||
static const struct wl_message kde_output_device_mode_v2_events[] = {
|
||||
@@ -86,7 +108,7 @@ static const struct wl_message kde_output_device_mode_v2_events[] = {
|
||||
};
|
||||
|
||||
WL_EXPORT const struct wl_interface kde_output_device_mode_v2_interface = {
|
||||
"kde_output_device_mode_v2", 20,
|
||||
"kde_output_device_mode_v2", 21,
|
||||
0, NULL,
|
||||
5, kde_output_device_mode_v2_events,
|
||||
};
|
||||
|
||||
@@ -83,7 +83,7 @@ static void waylandKdeCurrentModeListener(void* data, FF_MAYBE_UNUSED struct kde
|
||||
static void waylandKdeScaleListener(void* data, FF_MAYBE_UNUSED struct kde_output_device_v2* _, wl_fixed_t scale)
|
||||
{
|
||||
WaylandDisplay* wldata = (WaylandDisplay*) data;
|
||||
wldata->scale = wl_fixed_to_double(scale);
|
||||
wldata->dpi = (uint32_t) scale * 3 / 8; // wl_fixed_to_double(scale) * 96;
|
||||
}
|
||||
|
||||
static void waylandKdeEdidListener(void* data, FF_MAYBE_UNUSED struct kde_output_device_v2* _, const char* raw)
|
||||
@@ -181,6 +181,7 @@ static struct kde_output_device_v2_listener outputListener = {
|
||||
.sharpness = (void*) stubListener,
|
||||
.priority = (void*) stubListener,
|
||||
.auto_brightness = (void*) stubListener,
|
||||
.removed = (void*) stubListener,
|
||||
};
|
||||
|
||||
const char* ffWaylandHandleKdeOutput(WaylandData* wldata, struct wl_registry* registry, uint32_t name, uint32_t version)
|
||||
@@ -192,7 +193,6 @@ const char* ffWaylandHandleKdeOutput(WaylandData* wldata, struct wl_registry* re
|
||||
FF_LIST_AUTO_DESTROY modes = ffListCreate(sizeof(WaylandKdeMode));
|
||||
WaylandDisplay display = {
|
||||
.parent = wldata,
|
||||
.scale = 1,
|
||||
.transform = WL_OUTPUT_TRANSFORM_NORMAL,
|
||||
.type = FF_DISPLAY_TYPE_UNKNOWN,
|
||||
.name = ffStrbufCreate(),
|
||||
@@ -223,8 +223,7 @@ const char* ffWaylandHandleKdeOutput(WaylandData* wldata, struct wl_registry* re
|
||||
(uint32_t) display.width,
|
||||
(uint32_t) display.height,
|
||||
display.refreshRate / 1000.0,
|
||||
(uint32_t) (display.width / display.scale + .5),
|
||||
(uint32_t) (display.height / display.scale + .5),
|
||||
display.dpi,
|
||||
(uint32_t) display.preferredWidth,
|
||||
(uint32_t) display.preferredHeight,
|
||||
display.preferredRefreshRate / 1000.0,
|
||||
|
||||
@@ -43,7 +43,7 @@ typedef struct WaylandDisplay
|
||||
int32_t preferredRefreshRate;
|
||||
int32_t physicalWidth;
|
||||
int32_t physicalHeight;
|
||||
double scale;
|
||||
uint32_t dpi;
|
||||
enum wl_output_transform transform;
|
||||
FFDisplayType type;
|
||||
FFstrbuf name;
|
||||
|
||||
@@ -12,7 +12,7 @@ static void waylandZwlrTransformListener(void* data, FF_MAYBE_UNUSED struct zwlr
|
||||
static void waylandZwlrScaleListener(void* data, FF_MAYBE_UNUSED struct zwlr_output_head_v1 *zwlr_output_head_v1, wl_fixed_t scale)
|
||||
{
|
||||
WaylandDisplay* wldata = (WaylandDisplay*) data;
|
||||
wldata->scale = wl_fixed_to_double(scale);
|
||||
wldata->dpi = (uint32_t) scale * 3 / 8; // wl_fixed_to_double(scale) * 96;
|
||||
}
|
||||
|
||||
typedef struct WaylandZwlrMode
|
||||
@@ -125,7 +125,6 @@ static void waylandHandleZwlrHead(void *data, FF_MAYBE_UNUSED struct zwlr_output
|
||||
FF_LIST_AUTO_DESTROY modes = ffListCreate(sizeof(WaylandZwlrMode));
|
||||
WaylandDisplay display = {
|
||||
.parent = wldata,
|
||||
.scale = 1,
|
||||
.transform = WL_OUTPUT_TRANSFORM_NORMAL,
|
||||
.type = FF_DISPLAY_TYPE_UNKNOWN,
|
||||
.name = ffStrbufCreate(),
|
||||
@@ -146,8 +145,7 @@ static void waylandHandleZwlrHead(void *data, FF_MAYBE_UNUSED struct zwlr_output
|
||||
(uint32_t) display.width,
|
||||
(uint32_t) display.height,
|
||||
display.refreshRate / 1000.0,
|
||||
(uint32_t) (display.width / display.scale + 0.5),
|
||||
(uint32_t) (display.height / display.scale + 0.5),
|
||||
(uint32_t) display.dpi,
|
||||
(uint32_t) display.preferredWidth,
|
||||
(uint32_t) display.preferredHeight,
|
||||
display.preferredRefreshRate / 1000.0,
|
||||
|
||||
@@ -302,7 +302,7 @@ static const char* getFromProcesses(FFDisplayServerResult* result)
|
||||
#elif __OpenBSD__
|
||||
kvm_t* kd = kvm_open(NULL, NULL, NULL, KVM_NO_FILES, NULL);
|
||||
int count = 0;
|
||||
const struct kinfo_proc* proc = kvm_getprocs(kd, KERN_PROC_UID, userId, sizeof(*proc), &count);
|
||||
const struct kinfo_proc* proc = kvm_getprocs(kd, KERN_PROC_UID, (int) userId, sizeof(*proc), &count);
|
||||
if (proc)
|
||||
{
|
||||
for (int i = 0; i < count; ++i)
|
||||
|
||||
@@ -93,7 +93,7 @@ static xcb_randr_get_output_property_reply_t* xcbRandrGetProperty(XcbRandrData*
|
||||
|
||||
static void xcbDetectWMfromEWMH(XcbRandrData* data, xcb_window_t rootWindow, FFDisplayServerResult* result)
|
||||
{
|
||||
if(result->wmProcessName.length > 0 || ffStrbufCompS(&result->wmProtocolName, FF_WM_PROTOCOL_WAYLAND) == 0)
|
||||
if(result->wmProcessName.length > 0 || ffStrbufEqualS(&result->wmProtocolName, FF_WM_PROTOCOL_WAYLAND))
|
||||
return;
|
||||
|
||||
FF_AUTO_FREE xcb_window_t* wmWindow = (xcb_window_t*) xcbGetProperty(data, rootWindow, "_NET_SUPPORTING_WM_CHECK");
|
||||
@@ -127,7 +127,7 @@ static void xcbFetchServerVendor(XcbRandrData* data, FFDisplayServerResult* resu
|
||||
}
|
||||
}
|
||||
|
||||
static bool xcbRandrHandleOutput(XcbRandrData* data, xcb_randr_output_t output, FFstrbuf* name, bool primary, FFDisplayType displayType, struct xcb_randr_get_screen_resources_current_reply_t* screenResources, uint8_t bitDepth, double scaleFactor)
|
||||
static bool xcbRandrHandleOutput(XcbRandrData* data, xcb_randr_output_t output, FFstrbuf* name, bool primary, FFDisplayType displayType, struct xcb_randr_get_screen_resources_current_reply_t* screenResources, uint8_t bitDepth, uint32_t dpi)
|
||||
{
|
||||
xcb_randr_get_output_info_cookie_t outputInfoCookie = data->ffxcb_randr_get_output_info(data->connection, output, XCB_CURRENT_TIME);
|
||||
FF_AUTO_FREE xcb_randr_get_output_info_reply_t* outputInfoReply = data->ffxcb_randr_get_output_info_reply(data->connection, outputInfoCookie, NULL);
|
||||
@@ -153,13 +153,13 @@ static bool xcbRandrHandleOutput(XcbRandrData* data, xcb_randr_output_t output,
|
||||
ffEdidGetName(edidData, name);
|
||||
}
|
||||
|
||||
uint8_t randrEmulation = 0;
|
||||
bool randrEmulation = false;
|
||||
FF_AUTO_FREE xcb_randr_get_output_property_reply_t* randrEmulationReply = xcbRandrGetProperty(data, output, "RANDR Emulation");
|
||||
if(randrEmulationReply)
|
||||
{
|
||||
int len = data->ffxcb_randr_get_output_property_data_length(randrEmulationReply);
|
||||
if(len >= 1)
|
||||
randrEmulation = data->ffxcb_randr_get_output_property_data(randrEmulationReply)[0];
|
||||
randrEmulation = !!data->ffxcb_randr_get_output_property_data(randrEmulationReply)[0];
|
||||
}
|
||||
|
||||
xcb_randr_get_crtc_info_cookie_t crtcInfoCookie = data->ffxcb_randr_get_crtc_info(data->connection, outputInfoReply->crtc, XCB_CURRENT_TIME);
|
||||
@@ -211,8 +211,7 @@ static bool xcbRandrHandleOutput(XcbRandrData* data, xcb_randr_output_t output,
|
||||
(uint32_t) (currentMode ? currentMode->width : crtcInfoReply->width),
|
||||
(uint32_t) (currentMode ? currentMode->height : crtcInfoReply->height),
|
||||
currentMode ? (double) currentMode->dot_clock / (double) ((uint32_t) currentMode->htotal * currentMode->vtotal) : 0,
|
||||
(uint32_t) (crtcInfoReply->width / scaleFactor + .5),
|
||||
(uint32_t) (crtcInfoReply->height / scaleFactor + .5),
|
||||
dpi,
|
||||
preferredMode ? (uint32_t) preferredMode->width : 0,
|
||||
preferredMode ? (uint32_t) preferredMode->height : 0,
|
||||
preferredMode ? (double) preferredMode->dot_clock / (double) ((uint32_t) preferredMode->htotal * preferredMode->vtotal) : 0,
|
||||
@@ -228,17 +227,27 @@ static bool xcbRandrHandleOutput(XcbRandrData* data, xcb_randr_output_t output,
|
||||
: (currentMode ? "xcb-randr-mode" : "xcb-randr-crtc")
|
||||
|
||||
);
|
||||
if (item && edidData && edidLength >= 128)
|
||||
if (item)
|
||||
{
|
||||
item->hdrStatus = ffEdidGetHdrCompatible(edidData, (uint32_t) edidLength) ? FF_DISPLAY_HDR_STATUS_SUPPORTED : FF_DISPLAY_HDR_STATUS_UNSUPPORTED;
|
||||
ffEdidGetSerialAndManufactureDate(edidData, &item->serial, &item->manufactureYear, &item->manufactureWeek);
|
||||
if (edidData && edidLength >= 128)
|
||||
{
|
||||
item->hdrStatus = ffEdidGetHdrCompatible(edidData, (uint32_t) edidLength) ? FF_DISPLAY_HDR_STATUS_SUPPORTED : FF_DISPLAY_HDR_STATUS_UNSUPPORTED;
|
||||
ffEdidGetSerialAndManufactureDate(edidData, &item->serial, &item->manufactureYear, &item->manufactureWeek);
|
||||
}
|
||||
item->bitDepth = bitDepth;
|
||||
if ((rotation == 90 || rotation == 180) && !randrEmulation)
|
||||
{
|
||||
// In XWayland mode, width / height has been swapped out of box
|
||||
uint32_t tmp = item->width;
|
||||
item->width = item->height;
|
||||
item->height = tmp;
|
||||
}
|
||||
}
|
||||
|
||||
return !!item;
|
||||
}
|
||||
|
||||
static bool xcbRandrHandleMonitor(XcbRandrData* data, xcb_randr_monitor_info_t* monitor, struct xcb_randr_get_screen_resources_current_reply_t* screenResources, uint8_t bitDepth, double scaleFactor)
|
||||
static bool xcbRandrHandleMonitor(XcbRandrData* data, xcb_randr_monitor_info_t* monitor, struct xcb_randr_get_screen_resources_current_reply_t* screenResources, uint8_t bitDepth, uint32_t dpi)
|
||||
{
|
||||
//for some reasons, we have to construct this our self
|
||||
xcb_randr_output_iterator_t outputIterator = {
|
||||
@@ -262,7 +271,7 @@ static bool xcbRandrHandleMonitor(XcbRandrData* data, xcb_randr_monitor_info_t*
|
||||
|
||||
while(outputIterator.rem > 0)
|
||||
{
|
||||
if(xcbRandrHandleOutput(data, *outputIterator.data, &name, monitor->primary, displayType, screenResources, bitDepth, scaleFactor))
|
||||
if(xcbRandrHandleOutput(data, *outputIterator.data, &name, monitor->primary, displayType, screenResources, bitDepth, dpi))
|
||||
foundOutput = true;
|
||||
data->ffxcb_randr_output_next(&outputIterator);
|
||||
}
|
||||
@@ -274,8 +283,7 @@ static bool xcbRandrHandleMonitor(XcbRandrData* data, xcb_randr_monitor_info_t*
|
||||
(uint32_t) monitor->width,
|
||||
(uint32_t) monitor->height,
|
||||
0,
|
||||
(uint32_t) (monitor->width / scaleFactor + .5),
|
||||
(uint32_t) (monitor->height / scaleFactor + .5),
|
||||
dpi,
|
||||
0, 0, 0,
|
||||
0,
|
||||
&name,
|
||||
@@ -301,13 +309,13 @@ static bool xcbRandrHandleMonitors(XcbRandrData* data, xcb_screen_t* screen)
|
||||
xcb_randr_get_screen_resources_current_cookie_t screenResourcesCookie = data->ffxcb_randr_get_screen_resources_current(data->connection, screen->root);
|
||||
FF_AUTO_FREE struct xcb_randr_get_screen_resources_current_reply_t* screenResources = data->ffxcb_randr_get_screen_resources_current_reply(data->connection, screenResourcesCookie, NULL);
|
||||
|
||||
double scaleFactor = 1;
|
||||
uint32_t dpi = 0;
|
||||
FF_AUTO_FREE const char* resourceManager = xcbGetProperty(data, screen->root, "RESOURCE_MANAGER");
|
||||
if (resourceManager)
|
||||
{
|
||||
FF_STRBUF_AUTO_DESTROY dpi = ffStrbufCreate();
|
||||
if (ffParsePropLines(resourceManager, "Xft.dpi:", &dpi))
|
||||
scaleFactor = ffStrbufToDouble(&dpi, 96) / 96;
|
||||
FF_STRBUF_AUTO_DESTROY dpiStr = ffStrbufCreate();
|
||||
if (ffParsePropLines(resourceManager, "Xft.dpi:", &dpiStr))
|
||||
dpi = (uint32_t) ffStrbufToUInt(&dpiStr, 96);
|
||||
}
|
||||
uint8_t bitDepth = (uint8_t) (screen->root_depth / 3);
|
||||
|
||||
@@ -317,7 +325,7 @@ static bool xcbRandrHandleMonitors(XcbRandrData* data, xcb_screen_t* screen)
|
||||
|
||||
while(monitorInfoIterator.rem > 0)
|
||||
{
|
||||
if(xcbRandrHandleMonitor(data, monitorInfoIterator.data, screenResources, bitDepth, scaleFactor))
|
||||
if(xcbRandrHandleMonitor(data, monitorInfoIterator.data, screenResources, bitDepth, dpi))
|
||||
foundMonitor = true;
|
||||
data->ffxcb_randr_monitor_info_next(&monitorInfoIterator);
|
||||
}
|
||||
@@ -337,8 +345,7 @@ static void xcbRandrHandleScreen(XcbRandrData* data, xcb_screen_t* screen)
|
||||
(uint32_t) screen->width_in_pixels,
|
||||
(uint32_t) screen->height_in_pixels,
|
||||
0,
|
||||
(uint32_t) screen->width_in_pixels,
|
||||
(uint32_t) screen->height_in_pixels,
|
||||
0,
|
||||
0, 0, 0,
|
||||
0,
|
||||
NULL,
|
||||
|
||||
@@ -76,7 +76,7 @@ static uint8_t* xrandrGetProperty(XrandrData* data, RROutput output, const char*
|
||||
|
||||
static void x11DetectWMFromEWMH(XrandrData* data, FFDisplayServerResult* result)
|
||||
{
|
||||
if(result->wmProcessName.length > 0 || ffStrbufCompS(&result->wmProtocolName, FF_WM_PROTOCOL_WAYLAND) == 0)
|
||||
if(result->wmProcessName.length > 0 || ffStrbufEqualS(&result->wmProtocolName, FF_WM_PROTOCOL_WAYLAND))
|
||||
return;
|
||||
|
||||
Window* wmWindow = (Window*) x11GetProperty(data, data->display, DefaultRootWindow(data->display), "_NET_SUPPORTING_WM_CHECK");
|
||||
@@ -101,7 +101,7 @@ static void x11FetchServerVendor(XrandrData* data, FFDisplayServerResult* result
|
||||
ffStrbufSetS(&result->wmProtocolName, serverVendor);
|
||||
}
|
||||
|
||||
static bool xrandrHandleCrtc(XrandrData* data, XRROutputInfo* output, FFstrbuf* name, bool primary, FFDisplayType displayType, uint8_t* edidData, uint32_t edidLength, XRRScreenResources* screenResources, uint8_t bitDepth, double scaleFactor, bool randrEmulation)
|
||||
static bool xrandrHandleCrtc(XrandrData* data, XRROutputInfo* output, FFstrbuf* name, bool primary, FFDisplayType displayType, uint8_t* edidData, uint32_t edidLength, XRRScreenResources* screenResources, uint8_t bitDepth, uint32_t dpi, bool randrEmulation)
|
||||
{
|
||||
//We do the check here, because we want the best fallback display if this call failed
|
||||
if(screenResources == NULL)
|
||||
@@ -145,8 +145,7 @@ static bool xrandrHandleCrtc(XrandrData* data, XRROutputInfo* output, FFstrbuf*
|
||||
(uint32_t) (currentMode ? currentMode->width : crtcInfo->width),
|
||||
(uint32_t) (currentMode ? currentMode->height : crtcInfo->height),
|
||||
currentMode ? (double) currentMode->dotClock / (double) ((uint32_t) currentMode->hTotal * currentMode->vTotal) : 0,
|
||||
(uint32_t) (crtcInfo->width / scaleFactor + .5),
|
||||
(uint32_t) (crtcInfo->height / scaleFactor + .5),
|
||||
dpi,
|
||||
preferredMode ? (uint32_t) preferredMode->width : 0,
|
||||
preferredMode ? (uint32_t) preferredMode->height : 0,
|
||||
preferredMode ? (double) preferredMode->dotClock / (double) ((uint32_t) preferredMode->hTotal * preferredMode->vTotal) : 0,
|
||||
@@ -170,13 +169,20 @@ static bool xrandrHandleCrtc(XrandrData* data, XRROutputInfo* output, FFstrbuf*
|
||||
ffEdidGetSerialAndManufactureDate(edidData, &item->serial, &item->manufactureYear, &item->manufactureWeek);
|
||||
}
|
||||
item->bitDepth = bitDepth;
|
||||
if ((rotation == 90 || rotation == 180) && !randrEmulation)
|
||||
{
|
||||
// In XWayland mode, width / height has been swapped out of box
|
||||
uint32_t tmp = item->width;
|
||||
item->width = item->height;
|
||||
item->height = tmp;
|
||||
}
|
||||
}
|
||||
|
||||
data->ffXRRFreeCrtcInfo(crtcInfo);
|
||||
return !!item;
|
||||
}
|
||||
|
||||
static bool xrandrHandleOutput(XrandrData* data, RROutput output, FFstrbuf* name, bool primary, FFDisplayType displayType, XRRScreenResources* screenResources, uint8_t bitDepth, double scaleFactor)
|
||||
static bool xrandrHandleOutput(XrandrData* data, RROutput output, FFstrbuf* name, bool primary, FFDisplayType displayType, XRRScreenResources* screenResources, uint8_t bitDepth, uint32_t dpi)
|
||||
{
|
||||
XRROutputInfo* outputInfo = data->ffXRRGetOutputInfo(data->display, screenResources, output);
|
||||
if(outputInfo == NULL)
|
||||
@@ -195,7 +201,7 @@ static bool xrandrHandleOutput(XrandrData* data, RROutput output, FFstrbuf* name
|
||||
|
||||
uint8_t* randrEmulation = xrandrGetProperty(data, output, "RANDR Emulation", NULL);
|
||||
|
||||
bool res = xrandrHandleCrtc(data, outputInfo, name, primary, displayType, edidData, edidLength, screenResources, bitDepth, scaleFactor, randrEmulation ? !!randrEmulation[0] : false);
|
||||
bool res = xrandrHandleCrtc(data, outputInfo, name, primary, displayType, edidData, edidLength, screenResources, bitDepth, dpi, randrEmulation ? !!randrEmulation[0] : false);
|
||||
|
||||
if (edidData)
|
||||
data->ffXFree(edidData);
|
||||
@@ -206,7 +212,7 @@ static bool xrandrHandleOutput(XrandrData* data, RROutput output, FFstrbuf* name
|
||||
return res;
|
||||
}
|
||||
|
||||
static bool xrandrHandleMonitor(XrandrData* data, XRRMonitorInfo* monitorInfo, XRRScreenResources* screenResources, uint8_t bitDepth, double scaleFactor)
|
||||
static bool xrandrHandleMonitor(XrandrData* data, XRRMonitorInfo* monitorInfo, XRRScreenResources* screenResources, uint8_t bitDepth, uint32_t dpi)
|
||||
{
|
||||
bool foundOutput = false;
|
||||
char* xname = data->ffXGetAtomName(data->display, monitorInfo->name);
|
||||
@@ -215,7 +221,7 @@ static bool xrandrHandleMonitor(XrandrData* data, XRRMonitorInfo* monitorInfo, X
|
||||
FFDisplayType displayType = ffdsGetDisplayType(name.chars);
|
||||
for(int i = 0; i < monitorInfo->noutput; i++)
|
||||
{
|
||||
if(xrandrHandleOutput(data, monitorInfo->outputs[i], &name, monitorInfo->primary, displayType, screenResources, bitDepth, scaleFactor))
|
||||
if(xrandrHandleOutput(data, monitorInfo->outputs[i], &name, monitorInfo->primary, displayType, screenResources, bitDepth, dpi))
|
||||
foundOutput = true;
|
||||
}
|
||||
|
||||
@@ -226,8 +232,7 @@ static bool xrandrHandleMonitor(XrandrData* data, XRRMonitorInfo* monitorInfo, X
|
||||
(uint32_t) monitorInfo->width,
|
||||
(uint32_t) monitorInfo->height,
|
||||
0,
|
||||
(uint32_t) (monitorInfo->width / scaleFactor + .5),
|
||||
(uint32_t) (monitorInfo->height / scaleFactor + .5),
|
||||
dpi,
|
||||
0, 0, 0,
|
||||
0,
|
||||
&name,
|
||||
@@ -251,13 +256,13 @@ static bool xrandrHandleMonitors(XrandrData* data, Screen* screen)
|
||||
|
||||
XRRScreenResources* screenResources = data->ffXRRGetScreenResourcesCurrent(data->display, RootWindowOfScreen(screen));
|
||||
|
||||
double scaleFactor = 1;
|
||||
uint32_t dpi = 1;
|
||||
char* resourceManager = (char*) x11GetProperty(data, data->display, screen->root, "RESOURCE_MANAGER");
|
||||
if (resourceManager)
|
||||
{
|
||||
FF_STRBUF_AUTO_DESTROY dpi = ffStrbufCreate();
|
||||
if (ffParsePropLines(resourceManager, "Xft.dpi:", &dpi))
|
||||
scaleFactor = ffStrbufToDouble(&dpi, 96) / 96;
|
||||
FF_STRBUF_AUTO_DESTROY dpiStr = ffStrbufCreate();
|
||||
if (ffParsePropLines(resourceManager, "Xft.dpi:", &dpiStr))
|
||||
dpi = (uint32_t) ffStrbufToUInt(&dpiStr, 96);
|
||||
data->ffXFree(resourceManager);
|
||||
}
|
||||
uint8_t bitDepth = (uint8_t) (screen->root_depth / 3);
|
||||
@@ -266,7 +271,7 @@ static bool xrandrHandleMonitors(XrandrData* data, Screen* screen)
|
||||
|
||||
for(int i = 0; i < numberOfMonitors; i++)
|
||||
{
|
||||
if(xrandrHandleMonitor(data, &monitorInfos[i], screenResources, bitDepth, scaleFactor))
|
||||
if(xrandrHandleMonitor(data, &monitorInfos[i], screenResources, bitDepth, dpi))
|
||||
foundAMonitor = true;
|
||||
}
|
||||
|
||||
@@ -287,8 +292,7 @@ static void xrandrHandleScreen(XrandrData* data, Screen* screen)
|
||||
(uint32_t) WidthOfScreen(screen),
|
||||
(uint32_t) HeightOfScreen(screen),
|
||||
0,
|
||||
(uint32_t) WidthOfScreen(screen),
|
||||
(uint32_t) HeightOfScreen(screen),
|
||||
0,
|
||||
0, 0, 0,
|
||||
0,
|
||||
NULL,
|
||||
|
||||
@@ -15,7 +15,7 @@ static const char* detectDnsFromConf(const char* path, FFDNSOptions* options, FF
|
||||
FF_AUTO_CLOSE_FILE FILE* file = fopen(path, "r");
|
||||
if (!file)
|
||||
{
|
||||
FF_DEBUG("Failed to open %s: %m", path);
|
||||
FF_DEBUG("Failed to open %s: %s", path, strerror(errno));
|
||||
return "fopen(path, r) failed";
|
||||
}
|
||||
|
||||
|
||||
@@ -18,7 +18,7 @@ static const char* detectDnsFromConf(const char* path, FFDNSOptions* options, FF
|
||||
FF_AUTO_CLOSE_FILE FILE* file = fopen(path, "r");
|
||||
if (!file)
|
||||
{
|
||||
FF_DEBUG("Failed to open %s: %m", path);
|
||||
FF_DEBUG("Failed to open %s: %s", path, strerror(errno));
|
||||
return "fopen(path, r) failed";
|
||||
}
|
||||
|
||||
|
||||
@@ -51,15 +51,14 @@ const char* ffDetectDNS(FFDNSOptions* options, FFlist* results)
|
||||
{
|
||||
SOCKADDR_IN* ipv4 = (SOCKADDR_IN*) ifa->Address.lpSockaddr;
|
||||
ffStrbufInitA(item, INET_ADDRSTRLEN);
|
||||
inet_ntop(AF_INET, &ipv4->sin_addr, item->chars, item->allocated);
|
||||
item->length = (uint32_t) (RtlIpv4AddressToStringA(&ipv4->sin_addr, item->chars) - item->chars);
|
||||
}
|
||||
else if (ifa->Address.lpSockaddr->sa_family == AF_INET6)
|
||||
{
|
||||
SOCKADDR_IN6* ipv6 = (SOCKADDR_IN6*) ifa->Address.lpSockaddr;
|
||||
ffStrbufInitA(item, INET6_ADDRSTRLEN);
|
||||
inet_ntop(AF_INET6, &ipv6->sin6_addr, item->chars, item->allocated);
|
||||
item->length = (uint32_t) (RtlIpv6AddressToStringA(&ipv6->sin6_addr, item->chars) - item->chars);
|
||||
}
|
||||
ffStrbufRecalculateLength(item);
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
@@ -7,14 +7,6 @@
|
||||
|
||||
#include <stdlib.h>
|
||||
|
||||
#ifdef _WIN32
|
||||
static inline char* realpath(const char* restrict file_name, char* restrict resolved_name)
|
||||
{
|
||||
assert(resolved_name != NULL);
|
||||
return _fullpath(resolved_name, file_name, _MAX_PATH);
|
||||
}
|
||||
#endif
|
||||
|
||||
static bool extractNvimVersionFromBinary(const char* str, FF_MAYBE_UNUSED uint32_t len, void* userdata)
|
||||
{
|
||||
if (!ffStrStartsWith(str, "NVIM v")) return true;
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
#include "font.h"
|
||||
#include "common/windows/unicode.h"
|
||||
#include "common/windows/registry.h"
|
||||
|
||||
#include <windows.h>
|
||||
|
||||
@@ -25,17 +26,36 @@ static void generateString(FFFontResult* font)
|
||||
|
||||
const char* ffDetectFontImpl(FFFontResult* result)
|
||||
{
|
||||
NONCLIENTMETRICSW info = { .cbSize = sizeof(info) };
|
||||
if(!SystemParametersInfoW(SPI_GETNONCLIENTMETRICS, sizeof(info), &info, 0))
|
||||
return "SystemParametersInfoW(SPI_GETNONCLIENTMETRICS) failed";
|
||||
FF_AUTO_CLOSE_FD HANDLE hKey = NULL;
|
||||
if (!ffRegOpenKeyForRead(HKEY_CURRENT_USER, L"Control Panel\\Desktop\\WindowMetrics", &hKey, NULL))
|
||||
return "ffRegOpenKeyForRead(HKEY_CURRENT_USER\\Control Panel\\Desktop\\WindowMetrics) failed";
|
||||
|
||||
LOGFONTW* fonts[4] = { &info.lfCaptionFont, &info.lfMenuFont, &info.lfMessageFont, &info.lfStatusFont };
|
||||
LOGFONTW fonts[4];
|
||||
FFArgBuffer fontBuffers[4] = {
|
||||
{ .data = &fonts[0], .length = sizeof(fonts[0]) },
|
||||
{ .data = &fonts[1], .length = sizeof(fonts[1]) },
|
||||
{ .data = &fonts[2], .length = sizeof(fonts[2]) },
|
||||
{ .data = &fonts[3], .length = sizeof(fonts[3]) },
|
||||
};
|
||||
|
||||
for(uint32_t i = 0; i < ARRAY_SIZE(fonts); ++i)
|
||||
if (!ffRegReadValues(hKey, 4, (FFRegValueArg[]) {
|
||||
FF_ARG(fontBuffers[0], L"CaptionFont"),
|
||||
FF_ARG(fontBuffers[1], L"MenuFont"),
|
||||
FF_ARG(fontBuffers[2], L"MessageFont"),
|
||||
FF_ARG(fontBuffers[3], L"StatusFont"),
|
||||
}, NULL))
|
||||
return "ffRegReadValues(HKEY_CURRENT_USER\\Control Panel\\Desktop\\WindowMetrics) failed";
|
||||
|
||||
for (uint32_t i = 0; i < ARRAY_SIZE(fonts); ++i)
|
||||
{
|
||||
ffStrbufSetWS(&result->fonts[i], fonts[i]->lfFaceName);
|
||||
if(fonts[i]->lfHeight < 0)
|
||||
ffStrbufAppendF(&result->fonts[i], " (%dpt)", (int)-fonts[i]->lfHeight);
|
||||
if (fontBuffers[i].length != sizeof(LOGFONTW))
|
||||
continue; // Invalid data, skip
|
||||
|
||||
LOGFONTW* logFont = &fonts[i];
|
||||
|
||||
ffStrbufSetWS(&result->fonts[i], logFont->lfFaceName);
|
||||
if (logFont->lfHeight < 0)
|
||||
ffStrbufAppendF(&result->fonts[i], " (%dpt)", (int)-logFont->lfHeight);
|
||||
}
|
||||
|
||||
generateString(result);
|
||||
|
||||
@@ -3,7 +3,6 @@
|
||||
#include "common/mallocHelper.h"
|
||||
#include "common/windows/unicode.h"
|
||||
|
||||
#include <winternl.h>
|
||||
#include <windows.h>
|
||||
#include <hidsdi.h>
|
||||
|
||||
@@ -159,7 +158,7 @@ const char* ffDetectGamepad(FFlist* devices /* List of FFGamepadDevice */)
|
||||
OVERLAPPED overlapped = { };
|
||||
DWORD nBytes;
|
||||
if (ReadFile(hHidFile, reportBuffer, caps.InputReportByteLength, &nBytes, &overlapped) ||
|
||||
(WaitForSingleObject(hHidFile, FF_IO_TERM_RESP_WAIT_MS) == WAIT_OBJECT_0 && GetOverlappedResult(hHidFile, &overlapped, &nBytes, FALSE)))
|
||||
GetOverlappedResultEx(hHidFile, &overlapped, &nBytes, FF_IO_TERM_RESP_WAIT_MS, TRUE))
|
||||
{
|
||||
if (rdi.hid.dwVendorId == 0x054C)
|
||||
{
|
||||
|
||||
@@ -0,0 +1,439 @@
|
||||
#pragma once
|
||||
|
||||
#include <stdint.h>
|
||||
#include <limits.h>
|
||||
#include <assert.h>
|
||||
|
||||
#if _WIN32
|
||||
#include <ntdef.h>
|
||||
#include <windef.h>
|
||||
#else
|
||||
#include <sys/ioctl.h>
|
||||
#include <uchar.h>
|
||||
|
||||
typedef struct _LUID {
|
||||
uint32_t LowPart;
|
||||
uint32_t HighPart;
|
||||
} LUID;
|
||||
|
||||
typedef uint32_t UINT;
|
||||
typedef uint64_t ULONGLONG;
|
||||
typedef uint64_t UINT64;
|
||||
typedef uint32_t UINT32;
|
||||
typedef uint16_t UINT16;
|
||||
typedef uint32_t ULONG;
|
||||
typedef uint16_t USHORT;
|
||||
typedef uint8_t UCHAR;
|
||||
typedef void VOID;
|
||||
typedef char16_t WCHAR;
|
||||
typedef void* HANDLE;
|
||||
typedef uint8_t BYTE;
|
||||
typedef int32_t BOOL;
|
||||
typedef uint8_t BOOLEAN;
|
||||
typedef union {
|
||||
struct {
|
||||
uint32_t LowPart;
|
||||
int32_t HighPart;
|
||||
} u;
|
||||
int64_t QuadPart;
|
||||
} LARGE_INTEGER;
|
||||
typedef int32_t NTSTATUS; // 0 for success, -1 for failure
|
||||
#define NT_SUCCESS(Status) ((NTSTATUS)(Status) >= 0)
|
||||
#endif
|
||||
|
||||
#define D3DKMT_ALIGN64 __attribute__((aligned(8)))
|
||||
#define MAX_ENUM_ADAPTERS 16
|
||||
|
||||
typedef struct D3DKMT_HANDLE
|
||||
{
|
||||
union {
|
||||
struct {
|
||||
uint32_t Instance : 6;
|
||||
uint32_t Index : 24;
|
||||
uint32_t Unique : 2;
|
||||
};
|
||||
uint32_t Value;
|
||||
};
|
||||
} D3DKMT_HANDLE;
|
||||
|
||||
typedef struct _D3DKMT_OPENADAPTERFROMLUID
|
||||
{
|
||||
LUID AdapterLuid;
|
||||
D3DKMT_HANDLE hAdapter;
|
||||
} D3DKMT_OPENADAPTERFROMLUID;
|
||||
|
||||
typedef struct _D3DKMT_ADAPTERINFO
|
||||
{
|
||||
D3DKMT_HANDLE hAdapter;
|
||||
LUID AdapterLuid;
|
||||
ULONG NumOfSources;
|
||||
BOOL bPrecisePresentRegionsPreferred;
|
||||
} D3DKMT_ADAPTERINFO;
|
||||
|
||||
typedef struct _D3DKMT_ENUMADAPTERS2
|
||||
{
|
||||
ULONG NumAdapters; // in/out: On input, the count of the pAdapters array buffer. On output, the number of adapters enumerated.
|
||||
D3DKMT_ADAPTERINFO* pAdapters; // out: Array of enumerated adapters containing NumAdapters elements
|
||||
} D3DKMT_ENUMADAPTERS2;
|
||||
static_assert(sizeof(D3DKMT_ENUMADAPTERS2) ==
|
||||
#if SIZE_MAX == UINT64_MAX
|
||||
0x10
|
||||
#else
|
||||
0x08
|
||||
#endif
|
||||
, "D3DKMT_ENUMADAPTERS2 structure size mismatch");
|
||||
|
||||
typedef struct _D3DKMT_ADAPTERREGISTRYINFO
|
||||
{
|
||||
WCHAR AdapterString[260];
|
||||
WCHAR BiosString[260];
|
||||
WCHAR DacType[260];
|
||||
WCHAR ChipType[260];
|
||||
} D3DKMT_ADAPTERREGISTRYINFO;
|
||||
|
||||
typedef struct _D3DKMT_CLOSEADAPTER
|
||||
{
|
||||
D3DKMT_HANDLE hAdapter; // in: adapter handle
|
||||
} D3DKMT_CLOSEADAPTER;
|
||||
|
||||
typedef struct _D3DKMT_SEGMENTSIZEINFO
|
||||
{
|
||||
D3DKMT_ALIGN64 ULONGLONG DedicatedVideoMemorySize;
|
||||
D3DKMT_ALIGN64 ULONGLONG DedicatedSystemMemorySize;
|
||||
D3DKMT_ALIGN64 ULONGLONG SharedSystemMemorySize;
|
||||
} D3DKMT_SEGMENTSIZEINFO;
|
||||
|
||||
typedef struct _D3DKMT_ADAPTERTYPE
|
||||
{
|
||||
union
|
||||
{
|
||||
struct
|
||||
{
|
||||
UINT RenderSupported : 1; // WDDM 1.2, Windows 8
|
||||
UINT DisplaySupported : 1;
|
||||
UINT SoftwareDevice : 1;
|
||||
UINT PostDevice : 1;
|
||||
UINT HybridDiscrete : 1; // WDDM 1.3, Windows 8.1
|
||||
UINT HybridIntegrated : 1;
|
||||
UINT IndirectDisplayDevice : 1;
|
||||
UINT Paravirtualized : 1; // WDDM 2.3, Windows 10 Fall Creators Update (version 1709)
|
||||
UINT ACGSupported : 1;
|
||||
UINT SupportSetTimingsFromVidPn : 1;
|
||||
UINT Detachable : 1;
|
||||
UINT ComputeOnly : 1; // WDDM 2.6, Windows 10 May 2019 Update (Version 1903)
|
||||
UINT Prototype : 1;
|
||||
UINT RuntimePowerManagement : 1; // WDDM 2.9, Windows 10 Insider Preview "Iron"
|
||||
UINT Reserved : 18;
|
||||
};
|
||||
UINT Value;
|
||||
};
|
||||
} D3DKMT_ADAPTERTYPE;
|
||||
|
||||
typedef struct _D3DKMT_ADAPTERADDRESS
|
||||
{
|
||||
UINT BusNumber; // Bus number on which the physical device is located.
|
||||
UINT DeviceNumber; // Index of the physical device on the bus.
|
||||
UINT FunctionNumber; // Function number of the adapter on the physical device.
|
||||
} D3DKMT_ADAPTERADDRESS;
|
||||
|
||||
typedef struct _D3DKMT_DEVICE_IDS
|
||||
{
|
||||
UINT VendorID;
|
||||
UINT DeviceID;
|
||||
UINT SubVendorID;
|
||||
UINT SubSystemID;
|
||||
UINT RevisionID;
|
||||
UINT BusType;
|
||||
} D3DKMT_DEVICE_IDS;
|
||||
|
||||
typedef struct _D3DKMT_UMD_DRIVER_VERSION
|
||||
{
|
||||
D3DKMT_ALIGN64 LARGE_INTEGER DriverVersion;
|
||||
} D3DKMT_UMD_DRIVER_VERSION;
|
||||
|
||||
typedef struct _D3DKMT_QUERY_DEVICE_IDS
|
||||
{
|
||||
UINT PhysicalAdapterIndex; // in:
|
||||
D3DKMT_DEVICE_IDS DeviceIds; // out:
|
||||
} D3DKMT_QUERY_DEVICE_IDS;
|
||||
|
||||
typedef enum _QAI_DRIVERVERSION
|
||||
{
|
||||
KMT_DRIVERVERSION_WDDM_1_0 = 1000, // Windows Vista
|
||||
KMT_DRIVERVERSION_WDDM_1_1_PRERELEASE = 1102, // Windows Vista with prereleased Win7 features
|
||||
KMT_DRIVERVERSION_WDDM_1_1 = 1105, // Windows 7
|
||||
KMT_DRIVERVERSION_WDDM_1_2 = 1200, // Windows 8
|
||||
KMT_DRIVERVERSION_WDDM_1_3 = 1300, // Windows 8.1
|
||||
KMT_DRIVERVERSION_WDDM_2_0 = 2000, // Windows 10
|
||||
KMT_DRIVERVERSION_WDDM_2_1 = 2100, // Windows 10 (1607)
|
||||
KMT_DRIVERVERSION_WDDM_2_2 = 2200, // Windows 10 (1703)
|
||||
KMT_DRIVERVERSION_WDDM_2_3 = 2300, // Windows 10 (1709)
|
||||
KMT_DRIVERVERSION_WDDM_2_4 = 2400, // Windows 10 (1803)
|
||||
KMT_DRIVERVERSION_WDDM_2_5 = 2500, // Windows 10 (1809)
|
||||
KMT_DRIVERVERSION_WDDM_2_6 = 2600, // Windows 10 (1903)
|
||||
KMT_DRIVERVERSION_WDDM_2_7 = 2700, // Windows 10 (2004)
|
||||
KMT_DRIVERVERSION_WDDM_2_8 = 2800, // Windows 11 Insider Preview Manganese
|
||||
KMT_DRIVERVERSION_WDDM_2_9 = 2900, // Windows 11 Insider Preview Iron
|
||||
KMT_DRIVERVERSION_WDDM_3_0 = 3000, // Windows 11 (21H2)
|
||||
KMT_DRIVERVERSION_WDDM_3_1 = 3100, // Windows 11 (22H2)
|
||||
KMT_DRIVERVERSION_WDDM_3_2 = 3200, // Windows 11 (24H2)
|
||||
} D3DKMT_DRIVERVERSION;
|
||||
|
||||
typedef struct _D3DKMT_QUERY_ADAPTER_UNIQUE_GUID
|
||||
{
|
||||
WCHAR AdapterUniqueGUID[40];
|
||||
} D3DKMT_QUERY_ADAPTER_UNIQUE_GUID;
|
||||
|
||||
typedef enum _KMTQUERYADAPTERINFOTYPE
|
||||
{
|
||||
KMTQAITYPE_GETSEGMENTSIZE = 3,
|
||||
KMTQAITYPE_ADAPTERGUID = 4,
|
||||
KMTQAITYPE_ADAPTERADDRESS = 6,
|
||||
KMTQAITYPE_ADAPTERREGISTRYINFO = 8,
|
||||
KMTQAITYPE_DRIVERVERSION = 13,
|
||||
KMTQAITYPE_ADAPTERTYPE = 15, // WDDM 1.2, Windows 8
|
||||
KMTQAITYPE_UMD_DRIVER_VERSION = 18,
|
||||
KMTQAITYPE_NODEMETADATA = 25, // WDDM 2.0, Windows 10
|
||||
KMTQAITYPE_PHYSICALADAPTERDEVICEIDS = 31,
|
||||
KMTQAITYPE_QUERY_ADAPTER_UNIQUE_GUID = 60, // WDDM 2.4, Windows 10 (1803)
|
||||
// This reports the GUID string used by the adapter's registry key (DirectX and Video)
|
||||
} KMTQUERYADAPTERINFOTYPE;
|
||||
|
||||
typedef struct _D3DKMT_QUERYADAPTERINFO
|
||||
{
|
||||
D3DKMT_HANDLE hAdapter;
|
||||
KMTQUERYADAPTERINFOTYPE Type;
|
||||
VOID* pPrivateDriverData;
|
||||
UINT PrivateDriverDataSize;
|
||||
} D3DKMT_QUERYADAPTERINFO;
|
||||
|
||||
#define D3DKMT_MAX_SEGMENT_COUNT 32
|
||||
|
||||
typedef enum _D3DKMT_MEMORY_SEGMENT_GROUP
|
||||
{
|
||||
D3DKMT_MEMORY_SEGMENT_GROUP_LOCAL = 0,
|
||||
D3DKMT_MEMORY_SEGMENT_GROUP_NON_LOCAL = 1
|
||||
} D3DKMT_MEMORY_SEGMENT_GROUP;
|
||||
|
||||
typedef enum _D3DKMT_QUERYSTATISTICS_TYPE
|
||||
{
|
||||
D3DKMT_QUERYSTATISTICS_PHYSICAL_ADAPTER = 10, // WDDM 2.4, Windows 10 April 2018 Update (version 1803)
|
||||
D3DKMT_QUERYSTATISTICS_SEGMENT_GROUP_USAGE = 17, // WDDM 3.1, Windows 11 2022 Update (version 22H2)
|
||||
D3DKMT_QUERYSTATISTICS_NODE2 = 18,
|
||||
} D3DKMT_QUERYSTATISTICS_TYPE;
|
||||
|
||||
typedef struct _D3DKMT_QUERYSTATISTICS_QUERY_PHYSICAL_ADAPTER
|
||||
{
|
||||
ULONG PhysicalAdapterIndex;
|
||||
} D3DKMT_QUERYSTATISTICS_QUERY_PHYSICAL_ADAPTER;
|
||||
|
||||
typedef struct _D3DKMT_QUERYSTATISTICS_QUERY_NODE2
|
||||
{
|
||||
UINT16 PhysicalAdapterIndex;
|
||||
UINT16 NodeOrdinal;
|
||||
} D3DKMT_QUERYSTATISTICS_QUERY_NODE2;
|
||||
|
||||
typedef struct _D3DKMT_ADAPTER_PERFDATA
|
||||
{
|
||||
UINT32 PhysicalAdapterIndex; // in: The physical adapter index, in an LDA chain
|
||||
D3DKMT_ALIGN64 ULONGLONG MemoryFrequency; // out: Clock frequency of the memory in hertz
|
||||
D3DKMT_ALIGN64 ULONGLONG MaxMemoryFrequency; // out: Max memory clock frequency
|
||||
D3DKMT_ALIGN64 ULONGLONG MaxMemoryFrequencyOC; // out: Clock frequency of the memory while overclocked in hertz.
|
||||
D3DKMT_ALIGN64 ULONGLONG MemoryBandwidth; // out: Amount of memory transferred in bytes
|
||||
D3DKMT_ALIGN64 ULONGLONG PCIEBandwidth; // out: Amount of memory transferred over PCI-E in bytes
|
||||
ULONG FanRPM; // out: Fan rpm
|
||||
ULONG Power; // out: Power draw of the adapter in tenths of a percentage
|
||||
ULONG Temperature; // out: Temperature in deci-Celsius 1 = 0.1C
|
||||
UCHAR PowerStateOverride; // out: Overrides dxgkrnls power view of linked adapters.
|
||||
} D3DKMT_ADAPTER_PERFDATA;
|
||||
|
||||
typedef struct _D3DKMT_ADAPTER_PERFDATACAPS
|
||||
{
|
||||
UINT32 PhysicalAdapterIndex; // in: The physical adapter index, in an LDA chain
|
||||
D3DKMT_ALIGN64 ULONGLONG MaxMemoryBandwidth; // out: Max memory bandwidth in bytes for 1 second
|
||||
D3DKMT_ALIGN64 ULONGLONG MaxPCIEBandwidth; // out: Max pcie bandwidth in bytes for 1 second
|
||||
ULONG MaxFanRPM; // out: Max fan rpm
|
||||
ULONG TemperatureMax; // out: Max temperature before damage levels
|
||||
ULONG TemperatureWarning; // out: The temperature level where throttling begins.
|
||||
} D3DKMT_ADAPTER_PERFDATACAPS;
|
||||
|
||||
#define DXGK_MAX_GPUVERSION_NAME_LENGTH 32
|
||||
|
||||
typedef struct _D3DKMT_GPUVERSION
|
||||
{
|
||||
UINT32 PhysicalAdapterIndex; // in: The physical adapter index, in an LDA chain
|
||||
WCHAR BiosVersion[DXGK_MAX_GPUVERSION_NAME_LENGTH]; //out: The gpu bios version
|
||||
WCHAR GpuArchitecture[DXGK_MAX_GPUVERSION_NAME_LENGTH]; //out: The gpu architectures name.
|
||||
} D3DKMT_GPUVERSION;
|
||||
|
||||
typedef struct _D3DKMT_QUERYSTATISTICS_PHYSICAL_ADAPTER_INFORMATION
|
||||
{
|
||||
D3DKMT_ADAPTER_PERFDATA AdapterPerfData;
|
||||
D3DKMT_ADAPTER_PERFDATACAPS AdapterPerfDataCaps;
|
||||
D3DKMT_GPUVERSION GpuVersion;
|
||||
} D3DKMT_QUERYSTATISTICS_PHYSICAL_ADAPTER_INFORMATION;
|
||||
|
||||
typedef struct _D3DKMT_QUERYSTATISTICS_PROCESS_NODE_INFORMATION {
|
||||
D3DKMT_ALIGN64 UINT64 Reserved[34];
|
||||
} D3DKMT_QUERYSTATISTICS_PROCESS_NODE_INFORMATION;
|
||||
|
||||
typedef struct _D3DKMT_NODE_PERFDATA
|
||||
{
|
||||
UINT32 NodeOrdinal; // in: Node ordinal of the requested engine.
|
||||
UINT32 PhysicalAdapterIndex; // in: The physical adapter index, in an LDA chain
|
||||
D3DKMT_ALIGN64 ULONGLONG Frequency; // out: Clock frequency of the engine in hertz
|
||||
D3DKMT_ALIGN64 ULONGLONG MaxFrequency; // out: Max engine clock frequency
|
||||
D3DKMT_ALIGN64 ULONGLONG MaxFrequencyOC;// out: Max engine over clock frequency
|
||||
ULONG Voltage; // out: Voltage of the engine in milli volts mV
|
||||
ULONG VoltageMax; // out: Max voltage levels in milli volts.
|
||||
ULONG VoltageMaxOC; // out: Max voltage level while overclocked in milli volts.
|
||||
// WDDM 2.5
|
||||
D3DKMT_ALIGN64 ULONGLONG MaxTransitionLatency; // out: Max transition latency to change the frequency in 100 nanoseconds
|
||||
} D3DKMT_NODE_PERFDATA;
|
||||
|
||||
typedef struct _D3DKMT_QUERYSTATISTICS_NODE_INFORMATION {
|
||||
D3DKMT_QUERYSTATISTICS_PROCESS_NODE_INFORMATION GlobalInformation; //Global statistics
|
||||
D3DKMT_QUERYSTATISTICS_PROCESS_NODE_INFORMATION SystemInformation; //Statistics for system thread
|
||||
D3DKMT_NODE_PERFDATA NodePerfData;
|
||||
UINT32 Reserved[3];
|
||||
} D3DKMT_QUERYSTATISTICS_NODE_INFORMATION;
|
||||
|
||||
typedef struct _D3DKMT_QUERYSTATISTICS_MEMORY_USAGE
|
||||
{
|
||||
D3DKMT_ALIGN64 UINT64 AllocatedBytes;
|
||||
D3DKMT_ALIGN64 UINT64 FreeBytes;
|
||||
D3DKMT_ALIGN64 UINT64 ZeroBytes;
|
||||
D3DKMT_ALIGN64 UINT64 ModifiedBytes;
|
||||
D3DKMT_ALIGN64 UINT64 StandbyBytes;
|
||||
} D3DKMT_QUERYSTATISTICS_MEMORY_USAGE;
|
||||
|
||||
typedef union _D3DKMT_QUERYSTATISTICS_RESULT
|
||||
{
|
||||
D3DKMT_QUERYSTATISTICS_PHYSICAL_ADAPTER_INFORMATION PhysAdapterInformation;
|
||||
D3DKMT_QUERYSTATISTICS_NODE_INFORMATION NodeInformation;
|
||||
D3DKMT_QUERYSTATISTICS_MEMORY_USAGE SegmentGroupUsageInformation;
|
||||
uint8_t Padding[776];
|
||||
} D3DKMT_QUERYSTATISTICS_RESULT;
|
||||
|
||||
typedef struct _D3DKMT_QUERYSTATISTICS_QUERY_SEGMENT_GROUP_USAGE
|
||||
{
|
||||
UINT16 PhysicalAdapterIndex;
|
||||
UINT16 SegmentGroup; // D3DKMT_MEMORY_SEGMENT_GROUP
|
||||
} D3DKMT_QUERYSTATISTICS_QUERY_SEGMENT_GROUP_USAGE;
|
||||
|
||||
typedef struct _D3DKMT_QUERYSTATISTICS
|
||||
{
|
||||
D3DKMT_QUERYSTATISTICS_TYPE Type; // in: type of data requested
|
||||
LUID AdapterLuid; // in: adapter to get export / statistics from
|
||||
HANDLE* hProcess; // in: process to get statistics for, if required for this query type
|
||||
D3DKMT_QUERYSTATISTICS_RESULT QueryResult; // out: requested data
|
||||
|
||||
union
|
||||
{
|
||||
D3DKMT_QUERYSTATISTICS_QUERY_PHYSICAL_ADAPTER QueryPhysAdapter; // in: id of physical adapter to get statistics for
|
||||
D3DKMT_QUERYSTATISTICS_QUERY_SEGMENT_GROUP_USAGE QuerySegmentGroupUsage;
|
||||
D3DKMT_QUERYSTATISTICS_QUERY_NODE2 QueryNode2; // in: id of node to get statistics for
|
||||
};
|
||||
} D3DKMT_QUERYSTATISTICS;
|
||||
static_assert(sizeof(D3DKMT_QUERYSTATISTICS) ==
|
||||
#if SIZE_MAX == UINT64_MAX
|
||||
0x328
|
||||
#else
|
||||
0x320
|
||||
#endif
|
||||
, "D3DKMT_QUERYSTATISTICS structure size mismatch");
|
||||
|
||||
#define DXGK_MAX_METADATA_NAME_LENGTH 32
|
||||
typedef enum
|
||||
{
|
||||
DXGK_ENGINE_TYPE_OTHER,
|
||||
DXGK_ENGINE_TYPE_3D,
|
||||
DXGK_ENGINE_TYPE_VIDEO_DECODE,
|
||||
DXGK_ENGINE_TYPE_VIDEO_ENCODE,
|
||||
DXGK_ENGINE_TYPE_VIDEO_PROCESSING,
|
||||
DXGK_ENGINE_TYPE_SCENE_ASSEMBLY,
|
||||
DXGK_ENGINE_TYPE_COPY,
|
||||
DXGK_ENGINE_TYPE_OVERLAY,
|
||||
DXGK_ENGINE_TYPE_CRYPTO,
|
||||
DXGK_ENGINE_TYPE_VIDEO_CODEC,
|
||||
DXGK_ENGINE_TYPE_MAX
|
||||
} DXGK_ENGINE_TYPE;
|
||||
typedef struct _DXGK_NODEMETADATA_FLAGS
|
||||
{
|
||||
union
|
||||
{
|
||||
struct
|
||||
{
|
||||
UINT ContextSchedulingSupported : 1; // WDDM 2.2
|
||||
UINT RingBufferFenceRelease : 1; // WDDM 2.5
|
||||
UINT SupportTrackedWorkload : 1;
|
||||
UINT UserModeSubmission : 1;
|
||||
UINT SupportBuildTestCommandBuffer : 1; // WDDM 3.2
|
||||
UINT Reserved : 11;
|
||||
UINT MaxInFlightHwQueueBuffers : 16;
|
||||
};
|
||||
UINT32 Value;
|
||||
};
|
||||
} DXGK_NODEMETADATA_FLAGS;
|
||||
|
||||
typedef struct _DXGK_NODEMETADATA
|
||||
{
|
||||
DXGK_ENGINE_TYPE EngineType;
|
||||
WCHAR FriendlyName[DXGK_MAX_METADATA_NAME_LENGTH];
|
||||
DXGK_NODEMETADATA_FLAGS Flags; // WDDM 2.2
|
||||
BOOLEAN GpuMmuSupported; // WDDM 2.0 ???
|
||||
BOOLEAN IoMmuSupported;
|
||||
} __attribute__((packed)) DXGK_NODEMETADATA;
|
||||
|
||||
typedef struct _D3DKMT_NODEMETADATA
|
||||
{
|
||||
UINT NodeOrdinalAndAdapterIndex; // WDDMv2: High word is physical adapter index, low word is node ordinal
|
||||
DXGK_NODEMETADATA NodeData;
|
||||
} __attribute__((packed)) D3DKMT_NODEMETADATA;
|
||||
static_assert(sizeof(D3DKMT_NODEMETADATA) == 0x4E, "D3DKMT_NODEMETADATA structure size mismatch");
|
||||
|
||||
// Functions
|
||||
|
||||
#if _WIN32
|
||||
|
||||
EXTERN_C _Check_return_ NTSTATUS APIENTRY D3DKMTOpenAdapterFromLuid(_Inout_ CONST D3DKMT_OPENADAPTERFROMLUID*);
|
||||
EXTERN_C _Check_return_ NTSTATUS APIENTRY D3DKMTQueryAdapterInfo(_Inout_ CONST D3DKMT_QUERYADAPTERINFO*);
|
||||
EXTERN_C _Check_return_ NTSTATUS APIENTRY D3DKMTCloseAdapter(_In_ CONST D3DKMT_CLOSEADAPTER*);
|
||||
EXTERN_C _Check_return_ NTSTATUS APIENTRY D3DKMTEnumAdapters2(_Inout_ D3DKMT_ENUMADAPTERS2*);
|
||||
EXTERN_C _Check_return_ NTSTATUS APIENTRY D3DKMTQueryStatistics(_In_ CONST D3DKMT_QUERYSTATISTICS*);
|
||||
|
||||
#else
|
||||
|
||||
// Ref: https://github.com/microsoft/WSL2-Linux-Kernel/blob/linux-msft-wsl-6.6.y/include/uapi/misc/d3dkmthk.h
|
||||
#define LX_DXOPENADAPTERFROMLUID _IOWR(0x47, 0x01, D3DKMT_OPENADAPTERFROMLUID)
|
||||
#define LX_DXQUERYADAPTERINFO _IOWR(0x47, 0x09, D3DKMT_QUERYADAPTERINFO)
|
||||
#define LX_DXENUMADAPTERS2 _IOWR(0x47, 0x14, D3DKMT_ENUMADAPTERS2)
|
||||
#define LX_DXCLOSEADAPTER _IOWR(0x47, 0x15, D3DKMT_CLOSEADAPTER)
|
||||
#define LX_DXQUERYSTATISTICS _IOWR(0x47, 0x43, D3DKMT_QUERYSTATISTICS)
|
||||
|
||||
static inline NTSTATUS D3DKMTOpenAdapterFromLuid(int dxgfd, const D3DKMT_OPENADAPTERFROMLUID* params)
|
||||
{
|
||||
return ioctl(dxgfd, LX_DXOPENADAPTERFROMLUID, params);
|
||||
}
|
||||
|
||||
static inline NTSTATUS D3DKMTQueryAdapterInfo(int dxgfd, const D3DKMT_QUERYADAPTERINFO* params)
|
||||
{
|
||||
return ioctl(dxgfd, LX_DXQUERYADAPTERINFO, params);
|
||||
}
|
||||
|
||||
static inline NTSTATUS D3DKMTCloseAdapter(int dxgfd, const D3DKMT_CLOSEADAPTER* params)
|
||||
{
|
||||
return ioctl(dxgfd, LX_DXCLOSEADAPTER, params);
|
||||
}
|
||||
|
||||
static inline NTSTATUS D3DKMTEnumAdapters2(int dxgfd, D3DKMT_ENUMADAPTERS2* params)
|
||||
{
|
||||
return ioctl(dxgfd, LX_DXENUMADAPTERS2, params);
|
||||
}
|
||||
|
||||
static inline NTSTATUS D3DKMTQueryStatistics(int dxgfd, const D3DKMT_QUERYSTATISTICS* params)
|
||||
{
|
||||
return ioctl(dxgfd, LX_DXQUERYSTATISTICS, params);
|
||||
}
|
||||
|
||||
#endif
|
||||
@@ -656,10 +656,10 @@ static const char* pciDetectGPUs(const FFGPUOptions* options, FFlist* gpus)
|
||||
|
||||
const char* ffDetectGPUImpl(const FFGPUOptions* options, FFlist* gpus)
|
||||
{
|
||||
#ifdef FF_HAVE_DIRECTX_HEADERS
|
||||
const char* ffGPUDetectByDirectX(const FFGPUOptions* options, FFlist* gpus);
|
||||
if (ffGPUDetectByDirectX(options, gpus) == NULL)
|
||||
return NULL;
|
||||
#if __x86_64__ || __aarch64__
|
||||
const char* ffGPUDetectWsl2(const FFGPUOptions* options, FFlist* gpus);
|
||||
if (ffGPUDetectWsl2(options, gpus) == NULL)
|
||||
return NULL;
|
||||
#endif
|
||||
|
||||
if (options->detectionMethod == FF_GPU_DETECTION_METHOD_AUTO)
|
||||
|
||||
@@ -8,7 +8,7 @@
|
||||
#include <dev/pci/pcidevs.h>
|
||||
#include <sys/pciio.h>
|
||||
|
||||
static inline int pciReadConf(int fd, uint32_t bus, uint32_t device, uint32_t func, uint32_t reg, uint32_t* result)
|
||||
static inline int pciReadConf(int fd, uint8_t bus, uint8_t device, uint8_t func, uint8_t reg, uint32_t* result)
|
||||
{
|
||||
struct pci_io bdfr = {
|
||||
.pi_sel = {
|
||||
@@ -34,21 +34,21 @@ const char* ffDetectGPUImpl(FF_MAYBE_UNUSED const FFGPUOptions* options, FFlist*
|
||||
if (pcifd < 0)
|
||||
return "open(\"/dev/pci0\", O_RDONLY | O_CLOEXEC) failed";
|
||||
|
||||
for (uint32_t bus = 0; bus <= 255; bus++)
|
||||
for (uint16_t bus = 0; bus <= 255; bus++)
|
||||
{
|
||||
for (uint32_t dev = 0; dev < 32; dev++)
|
||||
for (uint8_t dev = 0; dev < 32; dev++)
|
||||
{
|
||||
uint32_t maxfuncs = 0;
|
||||
for (uint32_t func = 0; func <= maxfuncs; func++)
|
||||
uint8_t maxfuncs = 0;
|
||||
for (uint8_t func = 0; func <= maxfuncs; func++)
|
||||
{
|
||||
uint32_t pciid, pciclass;
|
||||
if (pciReadConf(pcifd, bus, dev, func, PCI_ID_REG, &pciid) != 0)
|
||||
if (pciReadConf(pcifd, (uint8_t) bus, dev, func, PCI_ID_REG, &pciid) != 0)
|
||||
continue;
|
||||
|
||||
if (PCI_VENDOR(pciid) == PCI_VENDOR_INVALID || PCI_VENDOR(pciid) == 0)
|
||||
continue;
|
||||
|
||||
if (pciReadConf(pcifd, bus, dev, func, PCI_CLASS_REG, &pciclass) != 0)
|
||||
if (pciReadConf(pcifd, (uint8_t) bus, dev, func, PCI_CLASS_REG, &pciclass) != 0)
|
||||
continue;
|
||||
|
||||
if (func == 0)
|
||||
@@ -56,7 +56,7 @@ const char* ffDetectGPUImpl(FF_MAYBE_UNUSED const FFGPUOptions* options, FFlist*
|
||||
// For some reason, pciReadConf returns success even for non-existing devices.
|
||||
// So we need to check for `PCI_VENDOR(pciid) == PCI_VENDOR_INVALID` above to filter them out.
|
||||
uint32_t bhlcr;
|
||||
if (pciReadConf(pcifd, bus, dev, 0, PCI_BHLC_REG, &bhlcr) != 0)
|
||||
if (pciReadConf(pcifd, (uint8_t) bus, dev, 0, PCI_BHLC_REG, &bhlcr) != 0)
|
||||
continue;
|
||||
|
||||
if (PCI_HDRTYPE_MULTIFN(bhlcr)) maxfuncs = 7;
|
||||
|
||||
+165
-123
@@ -1,13 +1,14 @@
|
||||
#include "gpu.h"
|
||||
#include "detection/gpu/gpu_driver_specific.h"
|
||||
#include "common/library.h"
|
||||
#include "common/windows/unicode.h"
|
||||
#include "common/windows/registry.h"
|
||||
#include "common/mallocHelper.h"
|
||||
#include "common/debug.h"
|
||||
#include "common/windows/nt.h"
|
||||
|
||||
#include <windows.h>
|
||||
#include <cfgmgr32.h>
|
||||
#include "d3dkmthk.h"
|
||||
|
||||
#define FF_EMPTY_GUID_STR L"{00000000-0000-0000-0000-000000000000}"
|
||||
enum { FF_GUID_STRLEN = sizeof(FF_EMPTY_GUID_STR) / sizeof(wchar_t) - 1 };
|
||||
@@ -19,6 +20,13 @@ const uint32_t regDriverKeyPrefixLength = (uint32_t) __builtin_strlen("SYSTEM\\C
|
||||
|
||||
#define GUID_DEVCLASS_DISPLAY_STRING L"{4d36e968-e325-11ce-bfc1-08002be10318}" // Found in <devguid.h>
|
||||
|
||||
static inline void wrapRegCloseKey(HKEY* phKey)
|
||||
{
|
||||
if(*phKey)
|
||||
RegCloseKey(*phKey);
|
||||
}
|
||||
#define FF_HKEY_AUTO_DESTROY __attribute__((__cleanup__(wrapRegCloseKey)))
|
||||
|
||||
const char* ffDetectGPUImpl(FF_MAYBE_UNUSED const FFGPUOptions* options, FFlist* gpus)
|
||||
{
|
||||
FF_DEBUG("Starting GPU detection");
|
||||
@@ -115,7 +123,7 @@ const char* ffDetectGPUImpl(FF_MAYBE_UNUSED const FFGPUOptions* options, FFlist*
|
||||
|
||||
uint64_t adapterLuid = 0;
|
||||
|
||||
FF_HKEY_AUTO_DESTROY hVideoIdKey = NULL;
|
||||
FF_HKEY_AUTO_DESTROY HKEY hVideoIdKey = NULL;
|
||||
|
||||
wchar_t buffer[256];
|
||||
ULONG bufferLen = 0;
|
||||
@@ -147,7 +155,7 @@ const char* ffDetectGPUImpl(FF_MAYBE_UNUSED const FFGPUOptions* options, FFlist*
|
||||
{
|
||||
FF_DEBUG("Found VideoID: %ls", buffer);
|
||||
wmemcpy(regDirectxKey + regDirectxKeyPrefixLength, buffer, FF_GUID_STRLEN);
|
||||
FF_HKEY_AUTO_DESTROY hDirectxKey = NULL;
|
||||
FF_AUTO_CLOSE_FD HANDLE hDirectxKey = NULL;
|
||||
if (ffRegOpenKeyForRead(HKEY_LOCAL_MACHINE, regDirectxKey, &hDirectxKey, NULL))
|
||||
{
|
||||
FF_DEBUG("Opened DirectX registry key");
|
||||
@@ -237,7 +245,7 @@ const char* ffDetectGPUImpl(FF_MAYBE_UNUSED const FFGPUOptions* options, FFlist*
|
||||
{
|
||||
FF_DEBUG("Found driver GUID: %ls", buffer);
|
||||
wmemcpy(regDriverKey + regDriverKeyPrefixLength, buffer, FF_GUID_STRLEN + strlen("\\0000"));
|
||||
FF_HKEY_AUTO_DESTROY hRegDriverKey = NULL;
|
||||
FF_AUTO_CLOSE_FD HANDLE hRegDriverKey = NULL;
|
||||
if (ffRegOpenKeyForRead(HKEY_LOCAL_MACHINE, regDriverKey, &hRegDriverKey, NULL))
|
||||
{
|
||||
FF_DEBUG("Opened driver registry key");
|
||||
@@ -290,7 +298,7 @@ const char* ffDetectGPUImpl(FF_MAYBE_UNUSED const FFGPUOptions* options, FFlist*
|
||||
if (options->driverSpecific && getDriverSpecificDetectionFn(gpu->vendor.chars, &detectFn, &dllName))
|
||||
{
|
||||
FF_DEBUG("Calling driver-specific detection function for vendor: %s, DLL: %s", gpu->vendor.chars, dllName);
|
||||
detectFn(
|
||||
FF_MAYBE_UNUSED const char* error = detectFn(
|
||||
&(FFGpuDriverCondition) {
|
||||
.type = (deviceId > 0 ? FF_GPU_DRIVER_CONDITION_TYPE_DEVICE_ID : 0)
|
||||
| (adapterLuid > 0 ? FF_GPU_DRIVER_CONDITION_TYPE_LUID : 0)
|
||||
@@ -323,7 +331,7 @@ const char* ffDetectGPUImpl(FF_MAYBE_UNUSED const FFGPUOptions* options, FFlist*
|
||||
},
|
||||
dllName
|
||||
);
|
||||
FF_DEBUG("Driver-specific detection completed");
|
||||
FF_DEBUG("Driver-specific detection completed: %s", error ?: "Success");
|
||||
}
|
||||
else if (options->driverSpecific)
|
||||
{
|
||||
@@ -333,145 +341,179 @@ const char* ffDetectGPUImpl(FF_MAYBE_UNUSED const FFGPUOptions* options, FFlist*
|
||||
if (gpu->type == FF_GPU_TYPE_UNKNOWN && adapterLuid > 0)
|
||||
{
|
||||
FF_DEBUG("Trying to determine GPU type using D3DKMT APIs");
|
||||
#if !FF_WIN7_COMPAT
|
||||
D3DKMT_OPENADAPTERFROMLUID openAdapterFromLuid = { .AdapterLuid = *(LUID*)&adapterLuid };
|
||||
if (NT_SUCCESS(D3DKMTOpenAdapterFromLuid(&openAdapterFromLuid)))
|
||||
#else
|
||||
HMODULE hgdi32 = GetModuleHandleW(L"gdi32.dll");
|
||||
if (hgdi32)
|
||||
{
|
||||
FF_LIBRARY_LOAD_SYMBOL_LAZY(hgdi32, D3DKMTOpenAdapterFromLuid);
|
||||
if (ffD3DKMTOpenAdapterFromLuid) // Windows 8 and later
|
||||
FF_DEBUG("Successfully opened adapter from LUID");
|
||||
|
||||
D3DKMT_ADAPTERTYPE adapterType = {};
|
||||
D3DKMT_QUERYADAPTERINFO queryAdapterInfo = {
|
||||
.hAdapter = openAdapterFromLuid.hAdapter,
|
||||
.Type = KMTQAITYPE_ADAPTERTYPE, // Windows 8 and later
|
||||
.pPrivateDriverData = &adapterType,
|
||||
.PrivateDriverDataSize = sizeof(adapterType),
|
||||
};
|
||||
if (NT_SUCCESS(D3DKMTQueryAdapterInfo(&queryAdapterInfo)))
|
||||
{
|
||||
D3DKMT_OPENADAPTERFROMLUID openAdapterFromLuid = { .AdapterLuid = *(LUID*)&adapterLuid };
|
||||
if (NT_SUCCESS(ffD3DKMTOpenAdapterFromLuid(&openAdapterFromLuid)))
|
||||
#endif
|
||||
{
|
||||
FF_DEBUG("Successfully opened adapter from LUID");
|
||||
|
||||
D3DKMT_ADAPTERTYPE adapterType = {};
|
||||
D3DKMT_QUERYADAPTERINFO queryAdapterInfo = {
|
||||
.hAdapter = openAdapterFromLuid.hAdapter,
|
||||
.Type = KMTQAITYPE_ADAPTERTYPE, // Windows 8 and later
|
||||
.pPrivateDriverData = &adapterType,
|
||||
.PrivateDriverDataSize = sizeof(adapterType),
|
||||
};
|
||||
if (NT_SUCCESS(D3DKMTQueryAdapterInfo(&queryAdapterInfo)))
|
||||
{
|
||||
FF_DEBUG("Queried adapter type - HybridDiscrete: %d, HybridIntegrated: %d", adapterType.HybridDiscrete, adapterType.HybridIntegrated);
|
||||
if (adapterType.HybridDiscrete)
|
||||
gpu->type = FF_GPU_TYPE_DISCRETE;
|
||||
else if (adapterType.HybridIntegrated)
|
||||
gpu->type = FF_GPU_TYPE_INTEGRATED;
|
||||
}
|
||||
else
|
||||
{
|
||||
FF_DEBUG("Failed to query adapter type");
|
||||
}
|
||||
|
||||
if (gpu->frequency == FF_GPU_FREQUENCY_UNSET && ffIsWindows11OrGreater())
|
||||
{
|
||||
FF_DEBUG("Trying to get GPU frequency information");
|
||||
for (ULONG nodeIdx = 0; ; nodeIdx++)
|
||||
{
|
||||
D3DKMT_NODEMETADATA nodeMetadata = {
|
||||
.NodeOrdinalAndAdapterIndex = (0 << 16) | nodeIdx,
|
||||
};
|
||||
queryAdapterInfo = (D3DKMT_QUERYADAPTERINFO) {
|
||||
.hAdapter = openAdapterFromLuid.hAdapter,
|
||||
.Type = KMTQAITYPE_NODEMETADATA, // Windows 10 and later
|
||||
.pPrivateDriverData = &nodeMetadata,
|
||||
.PrivateDriverDataSize = sizeof(nodeMetadata),
|
||||
};
|
||||
if (!NT_SUCCESS(D3DKMTQueryAdapterInfo(&queryAdapterInfo)))
|
||||
{
|
||||
FF_DEBUG("No more nodes to query (index %lu)", nodeIdx);
|
||||
break;
|
||||
}
|
||||
if (nodeMetadata.NodeData.EngineType != DXGK_ENGINE_TYPE_3D)
|
||||
{
|
||||
FF_DEBUG("Skipping node %lu (not 3D engine)", nodeIdx);
|
||||
continue;
|
||||
}
|
||||
|
||||
D3DKMT_QUERYSTATISTICS queryStatistics = {
|
||||
.Type = D3DKMT_QUERYSTATISTICS_NODE2, // Windows 11 (22H2) and later
|
||||
.AdapterLuid = *(LUID*)&adapterLuid,
|
||||
.QueryNode2 = { .PhysicalAdapterIndex = 0, .NodeOrdinal = (UINT16) nodeIdx },
|
||||
};
|
||||
if (NT_SUCCESS(D3DKMTQueryStatistics(&queryStatistics)))
|
||||
{
|
||||
gpu->frequency = (uint32_t) (queryStatistics.QueryResult.NodeInformation.NodePerfData.MaxFrequency / 1000 / 1000);
|
||||
FF_DEBUG("Found GPU frequency: %u MHz", gpu->frequency);
|
||||
break;
|
||||
}
|
||||
else
|
||||
{
|
||||
FF_DEBUG("Failed to query node statistics for node %lu", nodeIdx);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
D3DKMT_CLOSEADAPTER closeAdapter = { .hAdapter = openAdapterFromLuid.hAdapter };
|
||||
(void) D3DKMTCloseAdapter(&closeAdapter);
|
||||
openAdapterFromLuid.hAdapter = 0;
|
||||
FF_DEBUG("Closed adapter handle");
|
||||
}
|
||||
else
|
||||
{
|
||||
FF_DEBUG("Failed to open adapter from LUID");
|
||||
}
|
||||
|
||||
if (options->temp && gpu->temperature == FF_GPU_TEMP_UNSET && ffIsWindows10OrGreater())
|
||||
{
|
||||
FF_DEBUG("Trying to get GPU temperature");
|
||||
D3DKMT_QUERYSTATISTICS queryStatistics = {
|
||||
.Type = D3DKMT_QUERYSTATISTICS_PHYSICAL_ADAPTER, // Windows 10 (1803) and later
|
||||
.AdapterLuid = *(LUID*)&adapterLuid,
|
||||
.QueryPhysAdapter = { .PhysicalAdapterIndex = 0 },
|
||||
};
|
||||
if (NT_SUCCESS(D3DKMTQueryStatistics(&queryStatistics)) &&
|
||||
queryStatistics.QueryResult.PhysAdapterInformation.AdapterPerfData.Temperature != 0)
|
||||
{
|
||||
gpu->temperature = queryStatistics.QueryResult.PhysAdapterInformation.AdapterPerfData.Temperature / 10.0;
|
||||
FF_DEBUG("Found GPU temperature: %.1f°C", gpu->temperature);
|
||||
}
|
||||
else
|
||||
{
|
||||
FF_DEBUG("Failed to get GPU temperature or temperature is 0");
|
||||
}
|
||||
}
|
||||
#if FF_WIN7_COMPAT
|
||||
FF_DEBUG("Queried adapter type - HybridDiscrete: %d, HybridIntegrated: %d", adapterType.HybridDiscrete, adapterType.HybridIntegrated);
|
||||
if (adapterType.HybridDiscrete)
|
||||
gpu->type = FF_GPU_TYPE_DISCRETE;
|
||||
else if (adapterType.HybridIntegrated)
|
||||
gpu->type = FF_GPU_TYPE_INTEGRATED;
|
||||
}
|
||||
else
|
||||
{
|
||||
FF_DEBUG("D3DKMTOpenAdapterFromLuid not available");
|
||||
FF_DEBUG("Failed to query adapter type");
|
||||
}
|
||||
|
||||
if (gpu->frequency == FF_GPU_FREQUENCY_UNSET && ffIsWindows11OrGreater())
|
||||
{
|
||||
FF_DEBUG("Trying to get GPU frequency information");
|
||||
for (ULONG nodeIdx = 0; ; nodeIdx++)
|
||||
{
|
||||
D3DKMT_NODEMETADATA nodeMetadata = {
|
||||
.NodeOrdinalAndAdapterIndex = (0 << 16) | nodeIdx,
|
||||
};
|
||||
queryAdapterInfo = (D3DKMT_QUERYADAPTERINFO) {
|
||||
.hAdapter = openAdapterFromLuid.hAdapter,
|
||||
.Type = KMTQAITYPE_NODEMETADATA, // Windows 10 and later
|
||||
.pPrivateDriverData = &nodeMetadata,
|
||||
.PrivateDriverDataSize = sizeof(nodeMetadata),
|
||||
};
|
||||
if (!NT_SUCCESS(D3DKMTQueryAdapterInfo(&queryAdapterInfo)))
|
||||
{
|
||||
FF_DEBUG("No more nodes to query (index %lu)", nodeIdx);
|
||||
break;
|
||||
}
|
||||
if (nodeMetadata.NodeData.EngineType != DXGK_ENGINE_TYPE_3D)
|
||||
{
|
||||
FF_DEBUG("Skipping node %lu (not 3D engine)", nodeIdx);
|
||||
continue;
|
||||
}
|
||||
|
||||
D3DKMT_QUERYSTATISTICS queryStatistics = {
|
||||
.Type = D3DKMT_QUERYSTATISTICS_NODE2, // Windows 11 (22H2) and later
|
||||
.AdapterLuid = *(LUID*)&adapterLuid,
|
||||
.QueryNode2 = { .PhysicalAdapterIndex = 0, .NodeOrdinal = (UINT16) nodeIdx },
|
||||
};
|
||||
if (NT_SUCCESS(D3DKMTQueryStatistics(&queryStatistics)))
|
||||
{
|
||||
gpu->frequency = (uint32_t) (queryStatistics.QueryResult.NodeInformation.NodePerfData.MaxFrequency / 1000 / 1000);
|
||||
FF_DEBUG("Found GPU frequency: %u MHz", gpu->frequency);
|
||||
break;
|
||||
}
|
||||
else
|
||||
{
|
||||
FF_DEBUG("Failed to query node statistics for node %lu", nodeIdx);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
D3DKMT_CLOSEADAPTER closeAdapter = { .hAdapter = openAdapterFromLuid.hAdapter };
|
||||
(void) D3DKMTCloseAdapter(&closeAdapter);
|
||||
openAdapterFromLuid.hAdapter = (D3DKMT_HANDLE) {};
|
||||
FF_DEBUG("Closed adapter handle");
|
||||
}
|
||||
else
|
||||
{
|
||||
FF_DEBUG("Failed to get gdi32.dll module handle");
|
||||
FF_DEBUG("Failed to open adapter from LUID");
|
||||
}
|
||||
|
||||
if (options->temp && gpu->temperature == FF_GPU_TEMP_UNSET && ffIsWindows10OrGreater())
|
||||
{
|
||||
FF_DEBUG("Trying to get GPU temperature");
|
||||
D3DKMT_QUERYSTATISTICS queryStatistics = {
|
||||
.Type = D3DKMT_QUERYSTATISTICS_PHYSICAL_ADAPTER, // Windows 10 (1803) and later
|
||||
.AdapterLuid = *(LUID*)&adapterLuid,
|
||||
.QueryPhysAdapter = { .PhysicalAdapterIndex = 0 },
|
||||
};
|
||||
if (NT_SUCCESS(D3DKMTQueryStatistics(&queryStatistics)) &&
|
||||
queryStatistics.QueryResult.PhysAdapterInformation.AdapterPerfData.Temperature != 0)
|
||||
{
|
||||
gpu->temperature = queryStatistics.QueryResult.PhysAdapterInformation.AdapterPerfData.Temperature / 10.0;
|
||||
FF_DEBUG("Found GPU temperature: %.1f°C", gpu->temperature);
|
||||
}
|
||||
else
|
||||
{
|
||||
FF_DEBUG("Failed to get GPU temperature or temperature is 0");
|
||||
}
|
||||
}
|
||||
|
||||
if (options->driverSpecific && gpu->dedicated.used == FF_GPU_VMEM_SIZE_UNSET && ffIsWindows11OrGreater())
|
||||
{
|
||||
FF_DEBUG("Trying to get used video memory from D3DKMT method");
|
||||
D3DKMT_QUERYSTATISTICS queryStatistics = {
|
||||
.Type = D3DKMT_QUERYSTATISTICS_SEGMENT_GROUP_USAGE,
|
||||
.AdapterLuid = *(LUID*)&adapterLuid,
|
||||
.QuerySegmentGroupUsage = {
|
||||
.PhysicalAdapterIndex = 0,
|
||||
.SegmentGroup = D3DKMT_MEMORY_SEGMENT_GROUP_LOCAL,
|
||||
},
|
||||
};
|
||||
if (NT_SUCCESS(D3DKMTQueryStatistics(&queryStatistics)))
|
||||
{
|
||||
D3DKMT_QUERYSTATISTICS_MEMORY_USAGE* info = &queryStatistics.QueryResult.SegmentGroupUsageInformation;
|
||||
uint64_t used = info->AllocatedBytes + info->ModifiedBytes + info->StandbyBytes;
|
||||
uint64_t total = used + info->FreeBytes + info->ZeroBytes;
|
||||
gpu->dedicated.used = used;
|
||||
gpu->dedicated.total = total;
|
||||
|
||||
FF_DEBUG("Found local memory size %llu / %llu", used, total);
|
||||
}
|
||||
else
|
||||
{
|
||||
FF_DEBUG("Failed to query segment group usage for local memory");
|
||||
}
|
||||
|
||||
queryStatistics.QuerySegmentGroupUsage.SegmentGroup = D3DKMT_MEMORY_SEGMENT_GROUP_NON_LOCAL;
|
||||
if (NT_SUCCESS(D3DKMTQueryStatistics(&queryStatistics)))
|
||||
{
|
||||
D3DKMT_QUERYSTATISTICS_MEMORY_USAGE* info = &queryStatistics.QueryResult.SegmentGroupUsageInformation;
|
||||
uint64_t used = info->AllocatedBytes + info->ModifiedBytes + info->StandbyBytes;
|
||||
uint64_t total = used + info->FreeBytes + info->ZeroBytes;
|
||||
gpu->shared.used = used;
|
||||
gpu->shared.total = total;
|
||||
|
||||
FF_DEBUG("Found non-local memory size %llu / %llu", used, total);
|
||||
}
|
||||
else
|
||||
{
|
||||
FF_DEBUG("Failed to query segment group usage for non-local memory");
|
||||
}
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
if (gpu->type == FF_GPU_TYPE_UNKNOWN)
|
||||
{
|
||||
FF_DEBUG("Using fallback GPU type detection");
|
||||
if (gpu->vendor.chars == FF_GPU_VENDOR_NAME_INTEL)
|
||||
if (gpu->vendor.chars == FF_GPU_VENDOR_NAME_NVIDIA)
|
||||
{
|
||||
gpu->type = gpu->deviceId == ffGPUPciAddr2Id(0, 0, 2, 0) ? FF_GPU_TYPE_INTEGRATED : FF_GPU_TYPE_DISCRETE;
|
||||
FF_DEBUG("Intel GPU type determined: %s", gpu->type == FF_GPU_TYPE_INTEGRATED ? "Integrated" : "Discrete");
|
||||
if (ffStrbufStartsWithIgnCaseS(&gpu->name, "GeForce") ||
|
||||
ffStrbufStartsWithIgnCaseS(&gpu->name, "Quadro") ||
|
||||
ffStrbufStartsWithIgnCaseS(&gpu->name, "Tesla"))
|
||||
gpu->type = FF_GPU_TYPE_DISCRETE;
|
||||
}
|
||||
else if (gpu->dedicated.total != FF_GPU_VMEM_SIZE_UNSET)
|
||||
else if (gpu->vendor.chars == FF_GPU_VENDOR_NAME_MTHREADS)
|
||||
{
|
||||
gpu->type = gpu->dedicated.total >= 1024 * 1024 * 1024 ? FF_GPU_TYPE_DISCRETE : FF_GPU_TYPE_INTEGRATED;
|
||||
FF_DEBUG("GPU type determined by memory size (%llu bytes): %s", gpu->dedicated.total, gpu->type == FF_GPU_TYPE_DISCRETE ? "Discrete" : "Integrated");
|
||||
if (ffStrbufStartsWithIgnCaseS(&gpu->name, "MTT "))
|
||||
gpu->type = FF_GPU_TYPE_DISCRETE;
|
||||
}
|
||||
else if (gpu->vendor.chars == FF_GPU_VENDOR_NAME_INTEL)
|
||||
{
|
||||
// 0000:00:02.0 is reserved for Intel integrated graphics
|
||||
gpu->type = gpu->deviceId == ffGPUPciAddr2Id(0, 0, 2, 0) ? FF_GPU_TYPE_INTEGRATED : FF_GPU_TYPE_DISCRETE;
|
||||
}
|
||||
|
||||
if (gpu->type != FF_GPU_TYPE_UNKNOWN)
|
||||
FF_DEBUG("Determined GPU type based on vendor (%s) and name: %u", gpu->vendor.chars, gpu->type);
|
||||
else if (ffIsWindows10OrGreater())
|
||||
{
|
||||
const char* ffGPUDetectTypeWithDXCore(LUID adapterLuid, FFGPUResult* gpu);
|
||||
FF_MAYBE_UNUSED const char* error = ffGPUDetectTypeWithDXCore(*(LUID*)&adapterLuid, gpu);
|
||||
FF_DEBUG("DXCore GPU type detection result: %s", error ?: "Success");
|
||||
}
|
||||
else
|
||||
{
|
||||
FF_DEBUG("Unable to determine GPU type");
|
||||
}
|
||||
FF_DEBUG("Unable to determine GPU type by any method for this adapter");
|
||||
}
|
||||
|
||||
FF_DEBUG("Completed processing GPU #%d - Vendor: %s, Name: %s, Type: %d", deviceCount, gpu->vendor.chars, gpu->name.chars, gpu->type);
|
||||
|
||||
@@ -0,0 +1,88 @@
|
||||
extern "C"
|
||||
{
|
||||
#include "gpu.h"
|
||||
#include "common/library.h"
|
||||
#include "common/debug.h"
|
||||
}
|
||||
|
||||
#if __has_include(<directx/dxcore.h>) && __has_include(<dxguids/dxguids.h>)
|
||||
|
||||
#include <directx/dxcore.h>
|
||||
#include <dxguids/dxguids.h>
|
||||
#include "common/windows/util.hpp"
|
||||
|
||||
static IDXCoreAdapterFactory* loadDxCoreFactory()
|
||||
{
|
||||
static bool initialized = false;
|
||||
static IDXCoreAdapterFactory* factory = nullptr;
|
||||
if (initialized)
|
||||
return factory; // Already loaded
|
||||
|
||||
initialized = true;
|
||||
FF_LIBRARY_LOAD(dxcore, NULL, "dxcore" FF_LIBRARY_EXTENSION, 1)
|
||||
|
||||
// DXCoreCreateAdapterFactory is a reloaded function, so we can't use FF_LIBRARY_LOAD_SYMBOL_MESSAGE here
|
||||
typedef HRESULT (*DXCoreCreateAdapterFactory_t)(REFIID riid, void** ppvFactory);
|
||||
|
||||
#ifndef FF_DISABLE_DLOPEN
|
||||
auto ffDXCoreCreateAdapterFactory = (DXCoreCreateAdapterFactory_t) dlsym(dxcore, "DXCoreCreateAdapterFactory");
|
||||
if (ffDXCoreCreateAdapterFactory == nullptr) return NULL;
|
||||
#else
|
||||
auto ffDXCoreCreateAdapterFactory = (DXCoreCreateAdapterFactory_t) DXCoreCreateAdapterFactory;
|
||||
#endif
|
||||
|
||||
HRESULT hr = ffDXCoreCreateAdapterFactory(IID_PPV_ARGS(&factory));
|
||||
if (FAILED(hr))
|
||||
{
|
||||
FF_DEBUG("DXCoreCreateAdapterFactory failed with HRESULT: 0x%08lX (%s)", hr, ffDebugHResult(hr));
|
||||
return NULL;
|
||||
}
|
||||
|
||||
dxcore = NULL; // Don't unload
|
||||
return factory;
|
||||
}
|
||||
|
||||
extern "C"
|
||||
const char* ffGPUDetectTypeWithDXCore(LUID adapterLuid, FFGPUResult* gpu)
|
||||
{
|
||||
auto* factory = loadDxCoreFactory();
|
||||
if (!factory)
|
||||
return "Failed to load DXCore library or create adapter factory";
|
||||
|
||||
IDXCoreAdapter *adapter = nullptr;
|
||||
HRESULT hr = factory->GetAdapterByLuid(adapterLuid, IID_PPV_ARGS(&adapter));
|
||||
if (FAILED(hr))
|
||||
{
|
||||
FF_DEBUG("GetAdapterByLuid failed with HRESULT: 0x%08lX (%s)", hr, ffDebugHResult(hr));
|
||||
return "Failed to get adapter by LUID";
|
||||
}
|
||||
|
||||
on_scope_exit releaseAdapter {[adapter] { adapter->Release(); }};
|
||||
|
||||
bool isIntegrated = false;
|
||||
hr = adapter->GetProperty(DXCoreAdapterProperty::IsIntegrated, sizeof(isIntegrated), &isIntegrated);
|
||||
if (FAILED(hr))
|
||||
{
|
||||
FF_DEBUG("GetProperty(IsIntegrated) failed with HRESULT: 0x%08lX (%s)", hr, ffDebugHResult(hr));
|
||||
return "Failed to get adapter properties";
|
||||
}
|
||||
|
||||
gpu->type = isIntegrated ? FF_GPU_TYPE_INTEGRATED : FF_GPU_TYPE_DISCRETE;
|
||||
FF_DEBUG("GPU type determined using DXCore: %s", isIntegrated ? "Integrated" : "Discrete");
|
||||
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
#else
|
||||
|
||||
#warning "DXCore headers not available, GPU type detection may be less accurate"
|
||||
|
||||
extern "C"
|
||||
const char* ffGPUDetectTypeWithDXCore(LUID adapterLuid, FFGPUResult* gpu)
|
||||
{
|
||||
FF_UNUSED(adapterLuid, gpu);
|
||||
FF_DEBUG("DXCore not available, skipping GPU type detection with DXCore");
|
||||
return "DXCore not available";
|
||||
}
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,377 @@
|
||||
#if __x86_64__ || __aarch64__ // WSL2 only supports x86_64 and aarch64
|
||||
|
||||
#include "detection/gpu/gpu.h"
|
||||
#include "detection/gpu/gpu_driver_specific.h"
|
||||
#include "common/io.h"
|
||||
#include "common/debug.h"
|
||||
|
||||
#include "d3dkmthk.h"
|
||||
#include <unistd.h>
|
||||
#include <fcntl.h>
|
||||
#include <sys/ioctl.h>
|
||||
#include <uchar.h>
|
||||
|
||||
static void ffStrbufAppendUtf16(FFstrbuf* strbuf, const char16_t* str)
|
||||
{
|
||||
mbstate_t state = {};
|
||||
while (*str)
|
||||
{
|
||||
char buf[5];
|
||||
size_t len = c16rtomb(buf, *str, &state);
|
||||
if (len == (size_t) -1)
|
||||
ffStrbufAppendS(strbuf, "�"); // U+FFFD REPLACEMENT CHARACTER
|
||||
else if (len > 0)
|
||||
ffStrbufAppendNS(strbuf, (uint32_t) len, buf);
|
||||
str++;
|
||||
}
|
||||
}
|
||||
|
||||
const char* ffGPUDetectWsl2(const FFGPUOptions* options, FFlist* gpus)
|
||||
{
|
||||
FF_AUTO_CLOSE_FD int dxg = open("/dev/dxg", O_RDWR); // Windows DXCore/D3DKMT adapter driver for WSL
|
||||
if (dxg < 0)
|
||||
{
|
||||
if (errno == ENOENT)
|
||||
{
|
||||
FF_DEBUG("/dev/dxg is not available, WSL DXCore GPU driver not detected");
|
||||
return "No DXCore GPU driver detected (no /dev/dxg)";
|
||||
}
|
||||
else
|
||||
{
|
||||
FF_DEBUG("Failed to open /dev/dxg: %s", strerror(errno));
|
||||
return "Failed to open /dev/dxg";
|
||||
}
|
||||
}
|
||||
|
||||
FF_DEBUG("Opened /dev/dxg successfully");
|
||||
|
||||
D3DKMT_ADAPTERINFO adapters[64];
|
||||
D3DKMT_ENUMADAPTERS2 enumAdapters = {
|
||||
.NumAdapters = ARRAY_SIZE(adapters),
|
||||
.pAdapters = adapters,
|
||||
};
|
||||
if (!NT_SUCCESS(D3DKMTEnumAdapters2(dxg, &enumAdapters)))
|
||||
{
|
||||
FF_DEBUG("D3DKMTEnumAdapters2 failed: %s", strerror(errno));
|
||||
return "Failed to enumerate adapters with D3DKMTEnumAdapters2";
|
||||
}
|
||||
|
||||
FF_DEBUG("D3DKMTEnumAdapters2 succeeded, adapter count: %u", enumAdapters.NumAdapters);
|
||||
|
||||
for (uint32_t i = 0; i < enumAdapters.NumAdapters; i++)
|
||||
{
|
||||
const D3DKMT_ADAPTERINFO* adapter = &adapters[i];
|
||||
FF_DEBUG("Processing adapter #%u", i);
|
||||
|
||||
D3DKMT_ADAPTERTYPE adapterType;
|
||||
if (!NT_SUCCESS(D3DKMTQueryAdapterInfo(dxg, &(D3DKMT_QUERYADAPTERINFO) {
|
||||
.hAdapter = adapter->hAdapter,
|
||||
.Type = KMTQAITYPE_ADAPTERTYPE,
|
||||
.pPrivateDriverData = &adapterType,
|
||||
.PrivateDriverDataSize = sizeof(adapterType),
|
||||
})))
|
||||
{
|
||||
FF_DEBUG("KMTQAITYPE_ADAPTERTYPE query failed for adapter #%u: %s", i, strerror(errno));
|
||||
continue;
|
||||
}
|
||||
if (adapterType.SoftwareDevice)
|
||||
{
|
||||
FF_DEBUG("Skipping software adapter #%u", i);
|
||||
continue;
|
||||
}
|
||||
|
||||
FFGPUResult* gpu = FF_LIST_ADD(FFGPUResult, *gpus);
|
||||
ffStrbufInit(&gpu->vendor);
|
||||
ffStrbufInit(&gpu->name);
|
||||
ffStrbufInit(&gpu->driver);
|
||||
ffStrbufInit(&gpu->platformApi);
|
||||
ffStrbufInit(&gpu->memoryType);
|
||||
gpu->index = FF_GPU_INDEX_UNSET;
|
||||
gpu->temperature = FF_GPU_TEMP_UNSET;
|
||||
gpu->coreCount = FF_GPU_CORE_COUNT_UNSET;
|
||||
gpu->coreUsage = FF_GPU_CORE_USAGE_UNSET;
|
||||
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;
|
||||
gpu->type = adapterType.HybridIntegrated
|
||||
? FF_GPU_TYPE_INTEGRATED
|
||||
: adapterType.HybridDiscrete
|
||||
? FF_GPU_TYPE_DISCRETE
|
||||
: FF_GPU_TYPE_UNKNOWN;
|
||||
|
||||
const char* vendorStr = NULL;
|
||||
|
||||
D3DKMT_QUERY_DEVICE_IDS deviceIds = { .PhysicalAdapterIndex = 0 };
|
||||
if (NT_SUCCESS(D3DKMTQueryAdapterInfo(dxg, &(D3DKMT_QUERYADAPTERINFO) {
|
||||
.hAdapter = adapter->hAdapter,
|
||||
.Type = KMTQAITYPE_PHYSICALADAPTERDEVICEIDS,
|
||||
.pPrivateDriverData = &deviceIds,
|
||||
.PrivateDriverDataSize = sizeof(deviceIds),
|
||||
})))
|
||||
{
|
||||
vendorStr = ffGPUGetVendorString(deviceIds.DeviceIds.VendorID);
|
||||
ffStrbufSetStatic(&gpu->vendor, vendorStr);
|
||||
FF_DEBUG("Adapter #%u vendor/device IDs: vendor=0x%04x device=0x%04x", i, deviceIds.DeviceIds.VendorID, deviceIds.DeviceIds.DeviceID);
|
||||
}
|
||||
else
|
||||
FF_DEBUG("KMTQAITYPE_PHYSICALADAPTERDEVICEIDS query failed for adapter #%u: %s", i, strerror(errno));
|
||||
|
||||
D3DKMT_ADAPTERADDRESS adapterAddress;
|
||||
if (NT_SUCCESS(D3DKMTQueryAdapterInfo(dxg, &(D3DKMT_QUERYADAPTERINFO) {
|
||||
.hAdapter = adapter->hAdapter,
|
||||
.Type = KMTQAITYPE_ADAPTERADDRESS,
|
||||
.pPrivateDriverData = &adapterAddress,
|
||||
.PrivateDriverDataSize = sizeof(adapterAddress),
|
||||
})))
|
||||
{
|
||||
gpu->deviceId = ffGPUPciAddr2Id(0, adapterAddress.BusNumber, adapterAddress.DeviceNumber, adapterAddress.FunctionNumber);
|
||||
FF_DEBUG("Adapter #%u PCI address: bus=%u device=%u function=%u", i, adapterAddress.BusNumber, adapterAddress.DeviceNumber, adapterAddress.FunctionNumber);
|
||||
}
|
||||
else
|
||||
{
|
||||
adapterAddress.BusNumber = -1u;
|
||||
gpu->deviceId = ffGPUGeneral2Id(((uint64_t)adapter->AdapterLuid.HighPart << 32) | (uint64_t)adapter->AdapterLuid.LowPart);
|
||||
FF_DEBUG("KMTQAITYPE_ADAPTERADDRESS query failed for adapter #%u, fallback to LUID-based deviceId: %s", i, strerror(errno));
|
||||
}
|
||||
|
||||
D3DKMT_UMD_DRIVER_VERSION umdDriverVersion;
|
||||
if (NT_SUCCESS(D3DKMTQueryAdapterInfo(dxg, &(D3DKMT_QUERYADAPTERINFO) {
|
||||
.hAdapter = adapter->hAdapter,
|
||||
.Type = KMTQAITYPE_UMD_DRIVER_VERSION,
|
||||
.pPrivateDriverData = &umdDriverVersion,
|
||||
.PrivateDriverDataSize = sizeof(umdDriverVersion),
|
||||
})))
|
||||
{
|
||||
ffStrbufSetF(&gpu->driver, "%lu.%lu.%lu.%lu",
|
||||
umdDriverVersion.DriverVersion.QuadPart >> 48ul & 0xFFFF,
|
||||
umdDriverVersion.DriverVersion.QuadPart >> 32ul & 0xFFFF,
|
||||
umdDriverVersion.DriverVersion.QuadPart >> 16ul & 0xFFFF,
|
||||
umdDriverVersion.DriverVersion.QuadPart >> 0ul & 0xFFFF);
|
||||
FF_DEBUG("Adapter #%u UMD driver version: %08lX", i, (unsigned long) umdDriverVersion.DriverVersion.QuadPart);
|
||||
}
|
||||
else
|
||||
{
|
||||
FF_DEBUG("KMTQAITYPE_UMD_DRIVER_VERSION query failed for adapter #%u: %s", i, strerror(errno));
|
||||
}
|
||||
|
||||
D3DKMT_DRIVERVERSION wddmVersion = KMT_DRIVERVERSION_WDDM_3_0;
|
||||
if (NT_SUCCESS(D3DKMTQueryAdapterInfo(dxg, &(D3DKMT_QUERYADAPTERINFO) {
|
||||
.hAdapter = adapter->hAdapter,
|
||||
.Type = KMTQAITYPE_DRIVERVERSION,
|
||||
.pPrivateDriverData = &wddmVersion,
|
||||
.PrivateDriverDataSize = sizeof(wddmVersion),
|
||||
})))
|
||||
{
|
||||
ffStrbufSetF(&gpu->platformApi, "WDDM %u.%u", wddmVersion / 1000, (wddmVersion % 1000) / 100);
|
||||
FF_DEBUG("Adapter #%u WDDM version: %u", i, wddmVersion);
|
||||
}
|
||||
else
|
||||
{
|
||||
ffStrbufSetStatic(&gpu->platformApi, "WDDM");
|
||||
FF_DEBUG("KMTQAITYPE_DRIVERVERSION query failed for adapter #%u", i);
|
||||
}
|
||||
|
||||
if (gpu->name.length == 0)
|
||||
{
|
||||
D3DKMT_ADAPTERREGISTRYINFO registryInfo;
|
||||
if (NT_SUCCESS(D3DKMTQueryAdapterInfo(dxg, &(D3DKMT_QUERYADAPTERINFO) {
|
||||
.hAdapter = adapter->hAdapter,
|
||||
.Type = KMTQAITYPE_ADAPTERREGISTRYINFO,
|
||||
.pPrivateDriverData = ®istryInfo,
|
||||
.PrivateDriverDataSize = sizeof(registryInfo),
|
||||
})))
|
||||
{
|
||||
ffStrbufAppendUtf16(&gpu->name, registryInfo.AdapterString);
|
||||
FF_DEBUG("Adapter #%u adapter string: %s", i, gpu->name.chars);
|
||||
}
|
||||
else
|
||||
FF_DEBUG("KMTQAITYPE_ADAPTERREGISTRYINFO query failed for adapter #%u: %s", i, strerror(errno));
|
||||
}
|
||||
|
||||
if (vendorStr == FF_GPU_VENDOR_NAME_NVIDIA && options->driverSpecific)
|
||||
{
|
||||
FF_DEBUG("Trying NVIDIA driver-specific detection for adapter #%u", i);
|
||||
FFGpuDriverCondition cond = {
|
||||
.type = FF_GPU_DRIVER_CONDITION_TYPE_DEVICE_ID | (adapterAddress.DeviceNumber != -1u ? FF_GPU_DRIVER_CONDITION_TYPE_BUS_ID : 0),
|
||||
.pciDeviceId = {
|
||||
.deviceId = deviceIds.DeviceIds.DeviceID,
|
||||
.vendorId = deviceIds.DeviceIds.VendorID,
|
||||
.subSystemId = deviceIds.DeviceIds.SubSystemID,
|
||||
.revId = deviceIds.DeviceIds.RevisionID,
|
||||
},
|
||||
.pciBusId = {
|
||||
.domain = 0,
|
||||
.bus = adapterAddress.BusNumber,
|
||||
.device = adapterAddress.DeviceNumber,
|
||||
.func = adapterAddress.FunctionNumber,
|
||||
},
|
||||
};
|
||||
const char* error = ffDetectNvidiaGpuInfo(&cond, (FFGpuDriverResult){
|
||||
.index = &gpu->index,
|
||||
.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 = &gpu->name,
|
||||
}, "/usr/lib/wsl/lib/libnvidia-ml.so");
|
||||
if (error)
|
||||
FF_DEBUG("NVIDIA driver-specific detection failed for adapter #%u: %s", i, error);
|
||||
else
|
||||
FF_DEBUG("NVIDIA driver-specific detection succeeded for adapter #%u", i);
|
||||
}
|
||||
|
||||
if (gpu->dedicated.total == FF_GPU_VMEM_SIZE_UNSET && gpu->shared.total == FF_GPU_VMEM_SIZE_UNSET)
|
||||
{
|
||||
if (wddmVersion >= KMT_DRIVERVERSION_WDDM_3_1 && options->driverSpecific)
|
||||
{
|
||||
// Supports memory usage query; requires Windows 11 (22H2) or later
|
||||
D3DKMT_QUERYSTATISTICS queryStatistics = {
|
||||
.Type = D3DKMT_QUERYSTATISTICS_SEGMENT_GROUP_USAGE,
|
||||
.AdapterLuid = adapter->AdapterLuid,
|
||||
.QuerySegmentGroupUsage = {
|
||||
.PhysicalAdapterIndex = 0,
|
||||
.SegmentGroup = D3DKMT_MEMORY_SEGMENT_GROUP_LOCAL,
|
||||
},
|
||||
};
|
||||
if (NT_SUCCESS(D3DKMTQueryStatistics(dxg, &queryStatistics)))
|
||||
{
|
||||
D3DKMT_QUERYSTATISTICS_MEMORY_USAGE* info = &queryStatistics.QueryResult.SegmentGroupUsageInformation;
|
||||
uint64_t used = info->AllocatedBytes + info->ModifiedBytes + info->StandbyBytes;
|
||||
uint64_t total = used + info->FreeBytes + info->ZeroBytes;
|
||||
gpu->dedicated.used = used;
|
||||
gpu->dedicated.total = total;
|
||||
FF_DEBUG("Adapter #%u local memory usage: used=%lu total=%lu", i, (unsigned long) used, (unsigned long) total);
|
||||
}
|
||||
else
|
||||
FF_DEBUG("D3DKMT_QUERYSTATISTICS_SEGMENT_GROUP_USAGE (LOCAL) failed for adapter #%u: %s", i, strerror(errno));
|
||||
|
||||
queryStatistics.QuerySegmentGroupUsage.SegmentGroup = D3DKMT_MEMORY_SEGMENT_GROUP_NON_LOCAL;
|
||||
if (NT_SUCCESS(D3DKMTQueryStatistics(dxg, &queryStatistics)))
|
||||
{
|
||||
D3DKMT_QUERYSTATISTICS_MEMORY_USAGE* info = &queryStatistics.QueryResult.SegmentGroupUsageInformation;
|
||||
uint64_t used = info->AllocatedBytes + info->ModifiedBytes + info->StandbyBytes;
|
||||
uint64_t total = used + info->FreeBytes + info->ZeroBytes;
|
||||
gpu->shared.used = used;
|
||||
gpu->shared.total = total;
|
||||
FF_DEBUG("Adapter #%u non-local memory usage: used=%lu total=%lu", i, (unsigned long) used, (unsigned long) total);
|
||||
}
|
||||
else
|
||||
FF_DEBUG("D3DKMT_QUERYSTATISTICS_SEGMENT_GROUP_USAGE (NON_LOCAL) failed for adapter #%u: %s", i, strerror(errno));
|
||||
}
|
||||
else
|
||||
{
|
||||
// Supports basic segment (total) size query
|
||||
D3DKMT_SEGMENTSIZEINFO segmentSizeInfo = {};
|
||||
D3DKMT_QUERYADAPTERINFO queryAdapterInfo = {
|
||||
.hAdapter = adapter->hAdapter,
|
||||
.Type = KMTQAITYPE_GETSEGMENTSIZE,
|
||||
.pPrivateDriverData = &segmentSizeInfo,
|
||||
.PrivateDriverDataSize = sizeof(segmentSizeInfo),
|
||||
};
|
||||
if (NT_SUCCESS(D3DKMTQueryAdapterInfo(dxg, &queryAdapterInfo)))
|
||||
{
|
||||
FF_DEBUG("Adapter #%u segment size - DedicatedVideoMemorySize: %lu, DedicatedSystemMemorySize: %lu, SharedSystemMemorySize: %lu",
|
||||
i, segmentSizeInfo.DedicatedVideoMemorySize, segmentSizeInfo.DedicatedSystemMemorySize, segmentSizeInfo.SharedSystemMemorySize);
|
||||
gpu->dedicated.total = segmentSizeInfo.DedicatedVideoMemorySize;
|
||||
gpu->shared.total = segmentSizeInfo.DedicatedSystemMemorySize + segmentSizeInfo.SharedSystemMemorySize;
|
||||
}
|
||||
else
|
||||
{
|
||||
FF_DEBUG("Failed to query segment size information for adapter #%u: %s", i, strerror(errno));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (gpu->frequency == FF_GPU_FREQUENCY_UNSET && wddmVersion >= KMT_DRIVERVERSION_WDDM_3_1)
|
||||
{
|
||||
for (ULONG nodeIdx = 0; ; nodeIdx++)
|
||||
{
|
||||
D3DKMT_NODEMETADATA nodeMetadata = {
|
||||
.NodeOrdinalAndAdapterIndex = (0 << 16) | nodeIdx,
|
||||
};
|
||||
if (!NT_SUCCESS(D3DKMTQueryAdapterInfo(dxg, &(D3DKMT_QUERYADAPTERINFO) {
|
||||
.hAdapter = adapter->hAdapter,
|
||||
.Type = KMTQAITYPE_NODEMETADATA,
|
||||
.pPrivateDriverData = &nodeMetadata,
|
||||
.PrivateDriverDataSize = sizeof(nodeMetadata),
|
||||
})))
|
||||
break;
|
||||
|
||||
if (nodeMetadata.NodeData.EngineType != DXGK_ENGINE_TYPE_3D)
|
||||
continue;
|
||||
|
||||
D3DKMT_QUERYSTATISTICS queryStatistics = {
|
||||
.Type = D3DKMT_QUERYSTATISTICS_NODE2,
|
||||
.AdapterLuid = adapter->AdapterLuid,
|
||||
.QueryNode2 = { .PhysicalAdapterIndex = 0, .NodeOrdinal = (UINT16) nodeIdx },
|
||||
};
|
||||
if (NT_SUCCESS(D3DKMTQueryStatistics(dxg, &queryStatistics)))
|
||||
{
|
||||
gpu->frequency = (uint32_t) (queryStatistics.QueryResult.NodeInformation.NodePerfData.MaxFrequency / 1000 / 1000);
|
||||
FF_DEBUG("Adapter #%u max graphics frequency: %u MHz", i, gpu->frequency);
|
||||
break;
|
||||
}
|
||||
else
|
||||
FF_DEBUG("Failed to query node performance data for adapter #%u node #%u: %s", i, nodeIdx, strerror(errno));
|
||||
}
|
||||
}
|
||||
|
||||
if (options->temp && gpu->temperature == FF_GPU_TEMP_UNSET)
|
||||
{
|
||||
D3DKMT_QUERYSTATISTICS queryStatistics = {
|
||||
.Type = D3DKMT_QUERYSTATISTICS_PHYSICAL_ADAPTER,
|
||||
.AdapterLuid = adapter->AdapterLuid,
|
||||
.QueryPhysAdapter = { .PhysicalAdapterIndex = 0 },
|
||||
};
|
||||
if (NT_SUCCESS(D3DKMTQueryStatistics(dxg, &queryStatistics)) &&
|
||||
queryStatistics.QueryResult.PhysAdapterInformation.AdapterPerfData.Temperature != 0)
|
||||
{
|
||||
gpu->temperature = queryStatistics.QueryResult.PhysAdapterInformation.AdapterPerfData.Temperature / 10.0;
|
||||
FF_DEBUG("Adapter #%u temperature: %.1f°C", i, gpu->temperature);
|
||||
}
|
||||
else
|
||||
FF_DEBUG("Failed to query temperature for adapter #%u: %s", i, strerror(errno));
|
||||
}
|
||||
|
||||
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)
|
||||
{
|
||||
// 0000:00:02.0 is reserved for Intel integrated graphics
|
||||
gpu->type = gpu->deviceId == ffGPUPciAddr2Id(0, 0, 2, 0) ? FF_GPU_TYPE_INTEGRATED : FF_GPU_TYPE_DISCRETE;
|
||||
}
|
||||
}
|
||||
|
||||
FF_DEBUG("Adapter #%u summary: name='%s', vendor='%s', type=%u, deviceId=%lu",
|
||||
i,
|
||||
gpu->name.length ? gpu->name.chars : "unknown",
|
||||
gpu->vendor.length ? gpu->vendor.chars : "unknown",
|
||||
(unsigned) gpu->type,
|
||||
(unsigned long) gpu->deviceId);
|
||||
|
||||
if (NT_SUCCESS(D3DKMTCloseAdapter(dxg, &(D3DKMT_CLOSEADAPTER) {
|
||||
.hAdapter = adapter->hAdapter
|
||||
})))
|
||||
FF_DEBUG("Closed adapter #%u successfully", i);
|
||||
else
|
||||
FF_DEBUG("Failed to close adapter #%u: %s", i, strerror(errno));
|
||||
}
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
#endif
|
||||
@@ -1,138 +0,0 @@
|
||||
#ifdef FF_HAVE_DIRECTX_HEADERS
|
||||
|
||||
#define INITGUID
|
||||
extern "C" {
|
||||
#include "common/library.h"
|
||||
#include "detection/gpu/gpu.h"
|
||||
#include "detection/gpu/gpu_driver_specific.h"
|
||||
}
|
||||
#include "common/windows/util.hpp"
|
||||
|
||||
#include <wsl/winadapter.h>
|
||||
#include <directx/dxcore.h>
|
||||
#include <directx/d3d12.h>
|
||||
#include <dxguids/dxguids.h>
|
||||
#include <utility>
|
||||
#include <cinttypes>
|
||||
|
||||
#pragma GCC diagnostic ignored "-Wmissing-field-initializers"
|
||||
|
||||
extern "C"
|
||||
const char* ffGPUDetectByDirectX(FF_MAYBE_UNUSED const FFGPUOptions* options, FFlist* gpus)
|
||||
{
|
||||
FF_LIBRARY_LOAD_MESSAGE(libdxcore, "/usr/lib/wsl/lib/libdxcore" FF_LIBRARY_EXTENSION, 4)
|
||||
// DXCoreCreateAdapterFactory is a reloaded function, so we can't use FF_LIBRARY_LOAD_SYMBOL_MESSAGE here
|
||||
typedef HRESULT (*DXCoreCreateAdapterFactory_t)(REFIID riid, void** ppvFactory);
|
||||
|
||||
#ifndef FF_DISABLE_DLOPEN
|
||||
auto ffDXCoreCreateAdapterFactory = (DXCoreCreateAdapterFactory_t) dlsym(libdxcore, "DXCoreCreateAdapterFactory");
|
||||
if(ffDXCoreCreateAdapterFactory == nullptr) return "dlsym DXCoreCreateAdapterFactory failed";
|
||||
#else
|
||||
auto ffDXCoreCreateAdapterFactory = (DXCoreCreateAdapterFactory_t) DXCoreCreateAdapterFactory;
|
||||
#endif
|
||||
|
||||
IDXCoreAdapterFactory *factory = nullptr;
|
||||
|
||||
if (FAILED(ffDXCoreCreateAdapterFactory(IID_PPV_ARGS(&factory))))
|
||||
return "DXCoreCreateAdapterFactory(IID_PPV_ARGS(&factory)) failed";
|
||||
on_scope_exit destroyFactory([&] { factory->Release(); });
|
||||
|
||||
IDXCoreAdapterList *list = NULL;
|
||||
if (FAILED(factory->CreateAdapterList(1, &DXCORE_ADAPTER_ATTRIBUTE_D3D11_GRAPHICS, &list)))
|
||||
return "factory->CreateAdapterList(1, &DXCORE_ADAPTER_ATTRIBUTE_D3D11_GRAPHICS, &list) failed";
|
||||
on_scope_exit destroyList([&] { list->Release(); });
|
||||
|
||||
for (uint32_t i = 0, count = list->GetAdapterCount(); i < count; i++)
|
||||
{
|
||||
IDXCoreAdapter *adapter = nullptr;
|
||||
if (FAILED(list->GetAdapter(i, &adapter)))
|
||||
continue;
|
||||
on_scope_exit destroyAdapter([&] { adapter->Release(); });
|
||||
|
||||
// https://learn.microsoft.com/en-us/windows/win32/api/dxcore_interface/ne-dxcore_interface-dxcoreadapterproperty
|
||||
{
|
||||
bool value = 0;
|
||||
if (SUCCEEDED(adapter->GetProperty(DXCoreAdapterProperty::IsHardware, sizeof(value), &value)) && !value)
|
||||
continue;
|
||||
}
|
||||
|
||||
char desc[256];
|
||||
if (FAILED(adapter->GetProperty(DXCoreAdapterProperty::DriverDescription, sizeof(desc), desc)))
|
||||
continue;
|
||||
|
||||
FFGPUResult* gpu = (FFGPUResult*) ffListAdd(gpus);
|
||||
ffStrbufInitS(&gpu->name, desc);
|
||||
ffStrbufInit(&gpu->memoryType);
|
||||
gpu->index = FF_GPU_INDEX_UNSET;
|
||||
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");
|
||||
|
||||
LUID luid;
|
||||
if (SUCCEEDED(adapter->GetProperty(DXCoreAdapterProperty::InstanceLuid, sizeof(luid), &luid)))
|
||||
{
|
||||
static_assert(sizeof(luid) == sizeof(uint64_t), "LUID size mismatch");
|
||||
gpu->deviceId = ffGPUGeneral2Id((uint64_t) luid.HighPart << 32 | (uint64_t) luid.LowPart);
|
||||
}
|
||||
|
||||
ffStrbufInit(&gpu->driver);
|
||||
uint64_t value = 0;
|
||||
if (SUCCEEDED(adapter->GetProperty(DXCoreAdapterProperty::DriverVersion, sizeof(value), &value)))
|
||||
{
|
||||
ffStrbufSetF(&gpu->driver, "%" PRIu64 ".%" PRIu64 ".%" PRIu64 ".%" PRIu64,
|
||||
(value >> 48) & 0xFFFF,
|
||||
(value >> 32) & 0xFFFF,
|
||||
(value >> 16) & 0xFFFF,
|
||||
(value >> 0) & 0xFFFF);
|
||||
}
|
||||
|
||||
gpu->dedicated.used = gpu->shared.used = gpu->dedicated.total = gpu->shared.total = FF_GPU_VMEM_SIZE_UNSET;
|
||||
if (SUCCEEDED(adapter->GetProperty(DXCoreAdapterProperty::DedicatedAdapterMemory, sizeof(value), &value)))
|
||||
gpu->dedicated.total = value;
|
||||
if (SUCCEEDED(adapter->GetProperty(DXCoreAdapterProperty::SharedSystemMemory, sizeof(value), &value)))
|
||||
gpu->shared.total = value;
|
||||
|
||||
gpu->type = FF_GPU_TYPE_UNKNOWN;
|
||||
bool integrated;
|
||||
if (SUCCEEDED(adapter->GetProperty(DXCoreAdapterProperty::IsIntegrated, sizeof(integrated), &integrated)))
|
||||
gpu->type = integrated ? FF_GPU_TYPE_INTEGRATED : FF_GPU_TYPE_DISCRETE;
|
||||
|
||||
ffStrbufInit(&gpu->vendor);
|
||||
DXCoreHardwareID hardwareId;
|
||||
if (SUCCEEDED(adapter->GetProperty(DXCoreAdapterProperty::HardwareID, sizeof(hardwareId), &hardwareId)))
|
||||
{
|
||||
const char* vendorStr = ffGPUGetVendorString((unsigned) hardwareId.vendorID);
|
||||
ffStrbufSetStatic(&gpu->vendor, vendorStr);
|
||||
|
||||
if (vendorStr == FF_GPU_VENDOR_NAME_NVIDIA && (options->driverSpecific || options->temp))
|
||||
{
|
||||
FFGpuDriverCondition cond = {
|
||||
.type = FF_GPU_DRIVER_CONDITION_TYPE_DEVICE_ID,
|
||||
.pciDeviceId = {
|
||||
.deviceId = hardwareId.deviceID,
|
||||
.vendorId = hardwareId.vendorID,
|
||||
.subSystemId = hardwareId.subSysID,
|
||||
.revId = hardwareId.revision,
|
||||
},
|
||||
};
|
||||
ffDetectNvidiaGpuInfo(&cond, (FFGpuDriverResult){
|
||||
.index = &gpu->index,
|
||||
.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");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
#endif
|
||||
@@ -5,18 +5,13 @@
|
||||
|
||||
#include <stdlib.h>
|
||||
|
||||
static void getHostProductName(FFstrbuf* name)
|
||||
static bool getHostProductName(FFstrbuf* name)
|
||||
{
|
||||
if (ffReadFileBuffer("/sys/firmware/devicetree/base/model", name))
|
||||
if (ffReadFileBuffer("/sys/firmware/devicetree/base/model", name) ||
|
||||
ffReadFileBuffer("/sys/firmware/devicetree/base/banner-name", name))
|
||||
{
|
||||
ffStrbufTrimRight(name, '\0');
|
||||
return;
|
||||
}
|
||||
|
||||
if (ffReadFileBuffer("/sys/firmware/devicetree/base/banner-name", name))
|
||||
{
|
||||
ffStrbufTrimRight(name, '\0');
|
||||
return;
|
||||
return true;
|
||||
}
|
||||
|
||||
if (ffReadFileBuffer("/tmp/sysinfo/model", name))
|
||||
@@ -24,38 +19,61 @@ static void getHostProductName(FFstrbuf* name)
|
||||
ffStrbufTrimRightSpace(name);
|
||||
ffStrbufTrimRight(name, '\0');
|
||||
if(ffIsSmbiosValueSet(name))
|
||||
return;
|
||||
return true;
|
||||
}
|
||||
|
||||
ffStrbufClear(name);
|
||||
return false;
|
||||
}
|
||||
|
||||
static void getHostSerialNumber(FFstrbuf* serial)
|
||||
static bool getHostSerialNumber(FFstrbuf* serial)
|
||||
{
|
||||
if (ffReadFileBuffer("/sys/firmware/devicetree/base/smbios/smbios/system/serial", serial))
|
||||
ffStrbufTrimRight(serial, '\0');
|
||||
else if (ffReadFileBuffer("/sys/firmware/devicetree/base/serial-number", serial))
|
||||
if (ffReadFileBuffer("/sys/firmware/devicetree/base/smbios/smbios/system/serial", serial) ||
|
||||
ffReadFileBuffer("/sys/firmware/devicetree/base/serial-number", serial))
|
||||
{
|
||||
ffStrbufTrimRight(serial, '\0');
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
static bool getHostProductFamily(FFstrbuf* family)
|
||||
{
|
||||
if (ffReadFileBuffer("/sys/firmware/devicetree/base/smbios/smbios/system/family", family) ||
|
||||
ffReadFileBuffer("/sys/firmware/devicetree/base/smbios/smbios/system/product", family))
|
||||
{
|
||||
ffStrbufTrimRight(family, '\0');
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
static bool getHostVendor(FFstrbuf* vendor)
|
||||
{
|
||||
if (ffReadFileBuffer("/sys/firmware/devicetree/base/smbios/smbios/system/manufacturer", vendor))
|
||||
{
|
||||
ffStrbufTrimRight(vendor, '\0');
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
const char* ffDetectHost(FFHostResult* host)
|
||||
{
|
||||
if (!ffGetSmbiosValue("/sys/devices/virtual/dmi/id/product_family", "/sys/class/dmi/id/product_family", &host->family))
|
||||
// This is a hack for Asahi Linux, whose product_family is empty
|
||||
if (ffGetSmbiosValue("/sys/devices/virtual/dmi/id/product_family", "/sys/class/dmi/id/product_family", &host->family))
|
||||
{
|
||||
if (ffReadFileBuffer("/sys/firmware/devicetree/base/smbios/smbios/system/product", &host->family))
|
||||
ffStrbufTrimRight(&host->family, '\0');
|
||||
ffGetSmbiosValue("/sys/devices/virtual/dmi/id/product_name", "/sys/class/dmi/id/product_name", &host->name);
|
||||
ffGetSmbiosValue("/sys/devices/virtual/dmi/id/product_version", "/sys/class/dmi/id/product_version", &host->version);
|
||||
ffGetSmbiosValue("/sys/devices/virtual/dmi/id/product_sku", "/sys/class/dmi/id/product_sku", &host->sku);
|
||||
ffGetSmbiosValue("/sys/devices/virtual/dmi/id/product_serial", "/sys/class/dmi/id/product_serial", &host->serial);
|
||||
ffGetSmbiosValue("/sys/devices/virtual/dmi/id/sys_vendor", "/sys/class/dmi/id/sys_vendor", &host->vendor);
|
||||
}
|
||||
if (!ffGetSmbiosValue("/sys/devices/virtual/dmi/id/product_name", "/sys/class/dmi/id/product_name", &host->name))
|
||||
getHostProductName(&host->name);
|
||||
ffGetSmbiosValue("/sys/devices/virtual/dmi/id/product_version", "/sys/class/dmi/id/product_version", &host->version);
|
||||
ffGetSmbiosValue("/sys/devices/virtual/dmi/id/product_sku", "/sys/class/dmi/id/product_sku", &host->sku);
|
||||
if (!ffGetSmbiosValue("/sys/devices/virtual/dmi/id/product_serial", "/sys/class/dmi/id/product_serial", &host->serial))
|
||||
getHostSerialNumber(&host->serial);
|
||||
ffGetSmbiosValue("/sys/devices/virtual/dmi/id/product_uuid", "/sys/class/dmi/id/product_uuid", &host->uuid);
|
||||
if (!ffGetSmbiosValue("/sys/devices/virtual/dmi/id/sys_vendor", "/sys/class/dmi/id/sys_vendor", &host->vendor))
|
||||
else
|
||||
{
|
||||
if (ffReadFileBuffer("/sys/firmware/devicetree/base/smbios/smbios/system/manufacturer", &host->vendor))
|
||||
ffStrbufTrimRight(&host->vendor, '\0');
|
||||
getHostProductFamily(&host->family);
|
||||
getHostProductName(&host->name);
|
||||
getHostSerialNumber(&host->serial);
|
||||
getHostVendor(&host->vendor);
|
||||
}
|
||||
|
||||
#ifdef __x86_64__
|
||||
|
||||
@@ -107,6 +107,14 @@ const char* ffHostGetMacProductNameWithHwModel(const FFstrbuf* hwModel)
|
||||
else if(ffStrbufStartsWithS(hwModel, "Mac"))
|
||||
{
|
||||
const char* version = hwModel->chars + strlen("Mac");
|
||||
if(ffStrEquals(version, "17,9")) return "MacBook Pro (14-inch, M5 Pro, 2026)";
|
||||
if(ffStrEquals(version, "17,8")) return "MacBook Pro (16-inch, M5 Pro, 2026)";
|
||||
if(ffStrEquals(version, "17,7")) return "MacBook Pro (14-inch, M5 Max, 2026)";
|
||||
if(ffStrEquals(version, "17,6")) return "MacBook Pro (16-inch, M5 Max, 2026)";
|
||||
if(ffStrEquals(version, "17,5")) return "MacBook Neo (13-inch, A18 Pro, 2026)";
|
||||
if(ffStrEquals(version, "17,4")) return "MacBook Air (15-inch, M5, 2026)";
|
||||
if(ffStrEquals(version, "17,3")) return "MacBook Air (13-inch, M5, 2026)";
|
||||
if(ffStrEquals(version, "17,2")) return "MacBook Pro (14-inch, M5, 2025)";
|
||||
if(ffStrEquals(version, "16,13")) return "MacBook Air (15-inch, M4, 2025)";
|
||||
if(ffStrEquals(version, "16,12")) return "MacBook Air (13-inch, M4, 2025)";
|
||||
if(ffStrEquals(version, "16,11") ||
|
||||
|
||||
@@ -72,7 +72,7 @@ const char* ffDetectHost(FFHostResult* host)
|
||||
ffCleanUpSmbiosValue(&host->family);
|
||||
}
|
||||
|
||||
#if _WIN64 && __x86_64__ // aarch64 also defines _WIN64
|
||||
#if __x86_64__
|
||||
ffHostDetectMac(host);
|
||||
#endif
|
||||
|
||||
|
||||
@@ -3,10 +3,14 @@
|
||||
|
||||
const char* ffDetectIcons(FFIconsResult* result)
|
||||
{
|
||||
FF_HKEY_AUTO_DESTROY hKey = NULL;
|
||||
FF_AUTO_CLOSE_FD HANDLE hKey = NULL;
|
||||
if(!ffRegOpenKeyForRead(HKEY_CURRENT_USER, L"Software\\Microsoft\\Windows\\CurrentVersion\\Explorer\\HideDesktopIcons\\NewStartPanel", &hKey, NULL) &&
|
||||
!ffRegOpenKeyForRead(HKEY_CURRENT_USER, L"Software\\Microsoft\\Windows\\CurrentVersion\\Explorer\\HideDesktopIcons\\ClassicStartMenu", &hKey, NULL))
|
||||
return "ffRegOpenKeyForRead(Software\\Microsoft\\Windows\\CurrentVersion\\Explorer\\HideDesktopIcons\\{NewStartPanel|ClassicStartMenu}) failed";
|
||||
{
|
||||
// If the key doesn't exist, it means that the user never changed the default settings.
|
||||
ffStrbufSetStatic(&result->icons2, "Recycle Bin");
|
||||
return NULL;
|
||||
}
|
||||
|
||||
// Whether these icons are hidden
|
||||
uint32_t ThisPC = 1, UsersFiles = 1, RemoteNetwork = 1, RecycleBin = 0 /* Shown by default */, ControlPanel = 1;
|
||||
|
||||
@@ -2,55 +2,100 @@
|
||||
#include "common/io.h"
|
||||
#include "common/stringUtils.h"
|
||||
|
||||
#include <linux/input-event-codes.h>
|
||||
|
||||
const char* ffDetectKeyboard(FFlist* devices /* List of FFKeyboardDevice */)
|
||||
{
|
||||
// There is no /sys/class/input/kbd* on Linux
|
||||
FF_AUTO_CLOSE_DIR DIR* dirp = opendir("/dev/input/by-path/");
|
||||
if (dirp == NULL)
|
||||
return "opendir(\"/dev/input/by-path/\") == NULL";
|
||||
// Parse /proc/bus/input/devices to find keyboards with a "kbd" handler.
|
||||
// This detects both wired and Bluetooth keyboards uniformly.
|
||||
FF_STRBUF_AUTO_DESTROY content = ffStrbufCreate();
|
||||
if (!ffAppendFileBuffer("/proc/bus/input/devices", &content))
|
||||
return "ffAppendFileBuffer(\"/proc/bus/input/devices\") == NULL";
|
||||
|
||||
uint64_t flags = 0;
|
||||
FFstrbuf kbd = ffStrbufCreateStatic("kbd");
|
||||
|
||||
FF_STRBUF_AUTO_DESTROY path = ffStrbufCreate();
|
||||
FFKeyboardDevice device = {
|
||||
.name = ffStrbufCreate(),
|
||||
.serial = ffStrbufCreate()
|
||||
};
|
||||
|
||||
struct dirent* entry;
|
||||
while ((entry = readdir(dirp)) != NULL)
|
||||
char* line = NULL;
|
||||
size_t len = 0;
|
||||
while (ffStrbufGetline(&line, &len, &content))
|
||||
{
|
||||
if (!ffStrEndsWith(entry->d_name, "-event-kbd"))
|
||||
continue;
|
||||
|
||||
char buffer[32]; // `../eventXX`
|
||||
ssize_t len = readlinkat(dirfd(dirp), entry->d_name, buffer, ARRAY_SIZE(buffer) - 1);
|
||||
if (len <= (ssize_t) strlen("../event") || !ffStrStartsWith(buffer, "../event")) continue;
|
||||
buffer[len] = 0;
|
||||
|
||||
const char* eventid = buffer + strlen("../event");
|
||||
|
||||
char* pend = NULL;
|
||||
uint32_t index = (uint32_t) strtoul(eventid, &pend, 10);
|
||||
if (pend == eventid) continue;
|
||||
|
||||
// Ignore duplicate entries (flags supports up to 64 event indices)
|
||||
if (index >= 64 || (flags & (1UL << index)))
|
||||
continue;
|
||||
flags |= (1UL << index);
|
||||
|
||||
ffStrbufSetF(&path, "/sys/class/input/event%s/device/name", eventid);
|
||||
|
||||
FF_STRBUF_AUTO_DESTROY name = ffStrbufCreate();
|
||||
if (ffAppendFileBuffer(path.chars, &name))
|
||||
switch (line[0])
|
||||
{
|
||||
ffStrbufTrimRightSpace(&name);
|
||||
ffStrbufSubstrBefore(&path, path.length - (uint32_t) strlen("name"));
|
||||
case 'N': {
|
||||
const uint32_t prefixLen = strlen("N: Name=");
|
||||
if (__builtin_expect(len <= prefixLen, false)) continue;
|
||||
const char* name = line + prefixLen;
|
||||
const uint32_t nameLen = (uint32_t) len - prefixLen;
|
||||
ffStrbufSetNS(&device.name, nameLen, name);
|
||||
ffStrbufTrim(&device.name, '"');
|
||||
continue;
|
||||
}
|
||||
case 'H': {
|
||||
const uint32_t prefixLen = strlen("H: Handlers=");
|
||||
if (__builtin_expect(len <= prefixLen, false)) continue;
|
||||
const char* handlers = line + prefixLen;
|
||||
const uint32_t handlersLen = (uint32_t) len - prefixLen;
|
||||
if (!ffStrbufMatchSeparatedNS(&kbd, handlersLen, handlers, ' '))
|
||||
goto skipDevice;
|
||||
continue;
|
||||
}
|
||||
case 'B': {
|
||||
const char* bits = line + strlen("B: ");
|
||||
if (ffStrStartsWith(bits, "EV="))
|
||||
{
|
||||
// Check EV_REP (auto-repeat, bit 20) to filter pseudo-keyboards (Power Button, PC Speaker).
|
||||
const char* evBits = bits + strlen("EV=");
|
||||
uint64_t val = strtoull(evBits, NULL, 16);
|
||||
if (!(val & (1ULL << EV_REP)))
|
||||
goto skipDevice;
|
||||
}
|
||||
else if (ffStrStartsWith(bits, "KEY="))
|
||||
{
|
||||
// Check KEY_A (bit 30) to filter media remotes and headset controls.
|
||||
// The key capability bitmap is space-separated hex longs, MSB first;
|
||||
// KEY_A falls in the last (least significant) word on all architectures.
|
||||
const char* keyBits = bits + strlen("KEY=");
|
||||
const char* lastWord = memrchr(keyBits, ' ', len - (size_t) (keyBits - line));
|
||||
lastWord = lastWord ? lastWord + 1 : keyBits;
|
||||
|
||||
FFKeyboardDevice* device = (FFKeyboardDevice*) ffListAdd(devices);
|
||||
ffStrbufInitMove(&device->name, &name);
|
||||
ffStrbufInit(&device->serial);
|
||||
|
||||
ffStrbufAppendS(&path, "uniq");
|
||||
if (ffAppendFileBuffer(path.chars, &device->serial))
|
||||
ffStrbufTrimRightSpace(&device->serial);
|
||||
uint64_t val = strtoull(lastWord, NULL, 16);
|
||||
if (!(val & (1ULL << KEY_A)))
|
||||
goto skipDevice;
|
||||
}
|
||||
continue;
|
||||
}
|
||||
case 'U': {
|
||||
const uint32_t prefixLen = strlen("U: Uniq=");
|
||||
if (__builtin_expect(len <= prefixLen, false)) continue;
|
||||
const char* uniq = line + prefixLen;
|
||||
const uint32_t uniqLen = (uint32_t) len - prefixLen;
|
||||
ffStrbufSetNS(&device.serial, uniqLen, uniq);
|
||||
continue;
|
||||
}
|
||||
case '\0':
|
||||
// End of device entry; add to list if it has a name.
|
||||
if (device.name.length > 0)
|
||||
{
|
||||
FFKeyboardDevice* added = (FFKeyboardDevice*) ffListAdd(devices);
|
||||
ffStrbufInitMove(&added->name, &device.name);
|
||||
ffStrbufInitMove(&added->serial, &device.serial);
|
||||
}
|
||||
continue;
|
||||
default:
|
||||
continue;
|
||||
}
|
||||
|
||||
skipDevice:
|
||||
// Skip to the end of the current device entry.
|
||||
while (line[0] != '\0' && ffStrbufGetline(&line, &len, &content))
|
||||
;
|
||||
// Despite the fn name, it resets the string buffer to initial state
|
||||
ffStrbufDestroy(&device.name);
|
||||
ffStrbufDestroy(&device.serial);
|
||||
}
|
||||
|
||||
return NULL;
|
||||
|
||||
@@ -2,4 +2,4 @@
|
||||
|
||||
#include "fastfetch.h"
|
||||
|
||||
void ffDetectLocale(FFstrbuf* result);
|
||||
const char* ffDetectLocale(FFstrbuf* result);
|
||||
|
||||
@@ -2,15 +2,19 @@
|
||||
|
||||
#include <locale.h>
|
||||
|
||||
void ffDetectLocale(FFstrbuf* result)
|
||||
const char* ffDetectLocale(FFstrbuf* result)
|
||||
{
|
||||
ffStrbufAppendS(result, getenv("LC_ALL"));
|
||||
if(result->length > 0)
|
||||
return;
|
||||
return NULL;
|
||||
|
||||
ffStrbufAppendS(result, getenv("LANG"));
|
||||
if(result->length > 0)
|
||||
return;
|
||||
return NULL;
|
||||
|
||||
ffStrbufAppendS(result, setlocale(LC_TIME, NULL));
|
||||
if(result->length > 0)
|
||||
return NULL;
|
||||
|
||||
return "Failed to detect locale";
|
||||
}
|
||||
|
||||
@@ -2,12 +2,15 @@
|
||||
#include "common/windows/unicode.h"
|
||||
|
||||
#include <windows.h>
|
||||
#include <locale.h>
|
||||
|
||||
void ffDetectLocale(FFstrbuf* result)
|
||||
const char* ffDetectLocale(FFstrbuf* result)
|
||||
{
|
||||
wchar_t name[LOCALE_NAME_MAX_LENGTH];
|
||||
int size = GetUserDefaultLocaleName(name, LOCALE_NAME_MAX_LENGTH);
|
||||
if (size > 1) // including '\0'
|
||||
ffStrbufSetNWS(result, (uint32_t)size - 1, name);
|
||||
if (size <= 1) // including '\0'
|
||||
return "GetUserDefaultLocaleName() failed";
|
||||
|
||||
ffStrbufSetNWS(result, (uint32_t)size - 1, name);
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
@@ -4,16 +4,16 @@
|
||||
#include "modules/localip/option.h"
|
||||
|
||||
#ifndef IN6_IS_ADDR_GLOBAL
|
||||
#define IN6_IS_ADDR_GLOBAL(a) \
|
||||
((((const uint32_t *) (a))[0] & htonl(0x70000000)) == htonl(0x20000000))
|
||||
/* Global Unicast: 2000::/3 (001...) */
|
||||
#define IN6_IS_ADDR_GLOBAL(a) (((a)->s6_addr[0] & 0xE0) == 0x20)
|
||||
#endif
|
||||
#ifndef IN6_IS_ADDR_UNIQUE_LOCAL
|
||||
#define IN6_IS_ADDR_UNIQUE_LOCAL(a) \
|
||||
((((const uint32_t *) (a))[0] & htonl(0xfe000000)) == htonl(0xfc000000))
|
||||
/* Unique Local: fc00::/7 (1111 110...) */
|
||||
#define IN6_IS_ADDR_UNIQUE_LOCAL(a) (((a)->s6_addr[0] & 0xFE) == 0xFC)
|
||||
#endif
|
||||
#ifndef IN6_IS_ADDR_LINKLOCAL
|
||||
#define IN6_IS_ADDR_LINKLOCAL(a) \
|
||||
((((const uint32_t *) (a))[0] & htonl(0xffc00000)) == htonl(0xfe800000))
|
||||
/* Link-Local: fe80::/10 (1111 1110 10...) */
|
||||
#define IN6_IS_ADDR_LINKLOCAL(a) (((a)->s6_addr[0] == 0xFE) && (((a)->s6_addr[1] & 0xC0) == 0x80))
|
||||
#endif
|
||||
|
||||
typedef struct FFLocalIpResult
|
||||
|
||||
@@ -641,7 +641,7 @@ const char* ffDetectLocalIps(const FFLocalIpOptions* options, FFlist* results)
|
||||
ffStrCopy(ifmr.ifm_name, iface->name.chars, IFNAMSIZ);
|
||||
if (ioctl(sockfd, SIOCGIFMEDIA, &ifmr) == 0 && (IFM_TYPE(ifmr.ifm_active) & IFM_ETHER))
|
||||
{
|
||||
FF_DEBUG("Interface %s media type: 0x%x", iface->name.chars, IFM_SUBTYPE(ifmr.ifm_active));
|
||||
FF_DEBUG("Interface %s media type: 0x%x", iface->name.chars, (unsigned) IFM_SUBTYPE(ifmr.ifm_active));
|
||||
switch (IFM_SUBTYPE(ifmr.ifm_active))
|
||||
{
|
||||
#ifdef IFM_HPNA_1
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user