diff --git a/.github/ISSUE_TEMPLATE/bug_report_crash.yml b/.github/ISSUE_TEMPLATE/bug_report_crash.yml
index 1806e598f1..cd9678c2db 100644
--- a/.github/ISSUE_TEMPLATE/bug_report_crash.yml
+++ b/.github/ISSUE_TEMPLATE/bug_report_crash.yml
@@ -16,6 +16,15 @@ body:
options:
- label: I have checked the FAQ but the issue is not covered
required: true
+ - type: checkboxes
+ attributes:
+ label: Not a known issue
+ description: Please check if the issue is already known and being worked on.
+ options:
+ - label: I have checked the existing issues but the issue is not covered
+ required: true
+ - label: My issue is not about crashing on Fedora and KDE 6.6
+ required: true
- type: markdown
attributes:
value: "### General description of the bug"
diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml
index 0d46ae320f..d371317c8d 100644
--- a/.github/workflows/ci.yml
+++ b/.github/workflows/ci.yml
@@ -93,12 +93,12 @@ 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 libchafa-dev directx-headers-dev rpm ninja-build
+ 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
- name: install linuxbrew packages
run: |
/bin/bash -c "$(curl -fsSL https://raw.githubusercontent.com/Homebrew/install/HEAD/install.sh)"
- /home/linuxbrew/.linuxbrew/bin/brew install imagemagick --ignore-dependencies
+ /home/linuxbrew/.linuxbrew/bin/brew install imagemagick chafa --ignore-dependencies
- name: Initialize CodeQL
if: matrix.arch == 'amd64'
@@ -151,7 +151,7 @@ jobs:
cpack -V
- name: upload artifacts
- uses: actions/upload-artifact@v6
+ uses: actions/upload-artifact@v7
with:
name: fastfetch-linux-${{ matrix.arch }}
path: ./fastfetch-*.*
@@ -217,7 +217,7 @@ jobs:
run: ctest --output-on-failure
- name: upload artifacts
- uses: actions/upload-artifact@v6
+ uses: actions/upload-artifact@v7
with:
name: fastfetch-linux-i686
path: ./fastfetch-*.*
@@ -257,7 +257,7 @@ jobs:
ctest --output-on-failure
- name: upload artifacts
- uses: actions/upload-artifact@v6
+ uses: actions/upload-artifact@v7
with:
name: fastfetch-linux-armv7l
path: ./fastfetch-*.*
@@ -293,7 +293,7 @@ jobs:
ctest --output-on-failure
- name: upload artifacts
- uses: actions/upload-artifact@v6
+ uses: actions/upload-artifact@v7
with:
name: fastfetch-linux-armv6l
path: ./fastfetch-*.*
@@ -336,7 +336,7 @@ jobs:
ctest --output-on-failure
- name: upload artifacts
- uses: actions/upload-artifact@v6
+ uses: actions/upload-artifact@v7
with:
name: fastfetch-linux-${{ matrix.arch }}
path: ./fastfetch-*.*
@@ -374,7 +374,7 @@ jobs:
shell: alpine.sh {0}
- name: upload artifacts
- uses: actions/upload-artifact@v6
+ uses: actions/upload-artifact@v7
with:
name: fastfetch-musl-amd64
path: ./fastfetch-*.*
@@ -428,7 +428,7 @@ jobs:
run: ctest --output-on-failure
- name: upload artifacts
- uses: actions/upload-artifact@v6
+ uses: actions/upload-artifact@v7
with:
name: fastfetch-macos-${{ matrix.arch }}
path: ./fastfetch-*.*
@@ -444,6 +444,7 @@ jobs:
uses: vmactions/omnios-vm@v1
with:
usesh: true
+ envs: 'CMAKE_BUILD_TYPE'
prepare: |
uname -a
pkg update --accept
@@ -462,7 +463,7 @@ jobs:
cpack -V
- name: upload artifacts
- uses: actions/upload-artifact@v6
+ uses: actions/upload-artifact@v7
with:
name: fastfetch-omnios-amd64
path: ./fastfetch-*.*
@@ -478,6 +479,7 @@ jobs:
uses: vmactions/solaris-vm@v1
with:
usesh: true
+ envs: 'CMAKE_BUILD_TYPE'
release: "11.4-gcc-14"
prepare: |
uname -a
@@ -497,7 +499,7 @@ jobs:
cpack -V
- name: upload artifacts
- uses: actions/upload-artifact@v6
+ uses: actions/upload-artifact@v7
with:
name: fastfetch-solaris-amd64
path: ./fastfetch-*.*
@@ -520,6 +522,7 @@ jobs:
cpu_count: 4
shell: bash
version: '15.0'
+ environment_variables: 'CMAKE_BUILD_TYPE'
run: |
uname -a
sudo pkg update
@@ -534,7 +537,7 @@ jobs:
ctest --output-on-failure
- name: upload artifacts
- uses: actions/upload-artifact@v6
+ uses: actions/upload-artifact@v7
with:
name: fastfetch-freebsd-amd64
path: ./fastfetch-*.*
@@ -557,6 +560,7 @@ jobs:
cpu_count: 4
shell: bash
version: '7.8'
+ environment_variables: 'CMAKE_BUILD_TYPE'
run: |
uname -a
sudo pkg_add -u
@@ -571,7 +575,7 @@ jobs:
ctest --output-on-failure
- name: upload artifacts
- uses: actions/upload-artifact@v6
+ uses: actions/upload-artifact@v7
with:
name: fastfetch-openbsd-amd64
path: ./fastfetch-*.*
@@ -594,6 +598,7 @@ jobs:
cpu_count: 4
shell: bash
version: '10.1'
+ environment_variables: 'CMAKE_BUILD_TYPE'
run: |
uname -a
sudo pkgin -y install clang cmake git pkgconf wayland vulkan-headers dconf dbus sqlite3 ImageMagick
@@ -607,7 +612,7 @@ jobs:
ctest --output-on-failure
- name: upload artifacts
- uses: actions/upload-artifact@v6
+ uses: actions/upload-artifact@v7
with:
name: fastfetch-netbsd-amd64
path: ./fastfetch-*.*
@@ -626,6 +631,7 @@ jobs:
uses: vmactions/dragonflybsd-vm@v1
with:
usesh: yes
+ envs: 'CMAKE_BUILD_TYPE'
prepare: |
uname -a
pkg update
@@ -642,7 +648,7 @@ jobs:
ctest --output-on-failure
- name: upload artifacts
- uses: actions/upload-artifact@v6
+ uses: actions/upload-artifact@v7
with:
name: fastfetch-dragonfly-amd64
path: ./fastfetch-*.*
@@ -665,6 +671,7 @@ jobs:
architecture: x86-64
cpu_count: 4
shell: bash
+ environment_variables: 'CMAKE_BUILD_TYPE'
run: |
uname -a
pkgman install -y git dbus_devel mesa_devel libelf_devel imagemagick_devel opencl_headers ocl_icd_devel vulkan_devel zlib_devel chafa_devel cmake gcc make pkgconfig python3.10 || pkgman install -y git dbus_devel mesa_devel libelf_devel imagemagick_devel opencl_headers ocl_icd_devel vulkan_devel zlib_devel chafa_devel cmake gcc make pkgconfig python3.10
@@ -677,7 +684,7 @@ jobs:
ctest --output-on-failure
- name: upload artifacts
- uses: actions/upload-artifact@v6
+ uses: actions/upload-artifact@v7
with:
name: fastfetch-haiku-amd64
path: ./fastfetch-*.*
@@ -759,7 +766,7 @@ jobs:
- if: github.event_name == 'push' && github.repository == 'fastfetch-cli/fastfetch'
id: upload-unsigned-artifact
name: upload artifacts for signing
- uses: actions/upload-artifact@v6
+ uses: actions/upload-artifact@v7
with:
name: fastfetch-windows-${{ matrix.arch }}${{ matrix.win7-compat-postfix }}
path: |
@@ -786,7 +793,7 @@ jobs:
run: 7z a -t7z -mx9 -bd -y fastfetch-windows-${{ matrix.arch }}${{ matrix.win7-compat-postfix }}.7z LICENSE *.dll fastfetch.exe flashfetch.exe presets
- name: upload true artifacts
- uses: actions/upload-artifact@v6
+ uses: actions/upload-artifact@v7
with:
name: fastfetch-windows-${{ matrix.arch }}${{ matrix.win7-compat-postfix }}
path: ./fastfetch-windows-${{ matrix.arch }}${{ matrix.win7-compat-postfix }}.*
@@ -823,7 +830,7 @@ jobs:
- name: download artifacts
if: needs.linux-hosts.outputs.ffversion != steps.get_version_release.outputs.release
- uses: actions/download-artifact@v5
+ uses: actions/download-artifact@v8
- name: create release
if: needs.linux-hosts.outputs.ffversion != steps.get_version_release.outputs.release
diff --git a/CHANGELOG.md b/CHANGELOG.md
index d053e5ec1c..8a92a792a9 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -1,3 +1,35 @@
+# 2.60.0
+
+Changes:
+* The CMake option `ENABLE_WIN7_COMPAT:BOOLEAN` now defaults to `OFF` and will be removed in v2.61.0, effectively dropping support for Windows 7 in the next release.
+ * This follows the Windows 7 deprecation notice introduced in v2.57.0.
+* `wm.detectPlugin` now defaults to `true` (WM)
+
+Features:
+* Adds `{cwd}` for custom title formatting, which displays the current working directory (Title)
+* Adds support for detecting the Zed version (#2200, Editor)
+* Adds support for detecting `moss` packages (Packages, Linux)
+* Adds support for detecting komorebi, FancyWM, and GlazeWM (WM, Windows)
+* Adds support for WM plugin version detection on macOS (WM, macOS)
+* Adds support for retrieving the executable path on OpenBSD (#2195, OpenBSD)
+
+Bugfixes:
+* Fixes a potential segmentation fault caused by dereferencing a negative index (#2198)
+* Fixes `tempSensor` parsing so that it accepts only string values (#2202, CPU)
+* Fixes an issue that unexpectedly caused fewer devices to be reported (Keyboard, Linux)
+* Improves WM detection on LXQt by querying WM settings only when no WM has already been detected (#2199, WM, Linux)
+* Fixes memory leaks in DBus connection handling and in the OpenGL EGL context lifecycle
+* Fixes niri version detection on Fedora (WM, Linux)
+* Includes various internal cleanups and optimizations
+
+Logos:
+* Adds `RengeOS` (#2170)
+* Adds `Emmabuntüs` (#2207)
+* Updates Artix Linux (#2157)
+* Updates Linux Mint (#2186)
+* Renames `Refracted Devuan` to `Refracta`
+* Renames `ExodiaPredator` to `ExodiaOS`
+
# 2.59.0
Changes:
@@ -35,7 +67,7 @@ Features:
* Honors the `DBPath` and `RootDir` settings in `pacman.conf` when detecting Pacman packages (#2154, Packages, Linux)
Bugfixes:
-* Fixes a crash issues on KDE Plasma 6.6 (Display, Linux)
+* Fixes a crash issue on KDE Plasma 6.6 (Display, Linux)
* Fixes the Command module not working with `--dynamic-interval` (#2152, Command)
* Fixes Quartz Compositor version detection. It now correctly reports the version of `WindowServer` (`SkyLight`) instead of `WindowManager`. (WM, macOS)
@@ -62,7 +94,7 @@ Deprecation notice:
* Support for Windows 7 (and 8.x) is deprecated and will be removed in a future release. Extended support for Windows 7 (and 8.1) ended on January 10, 2023. These versions do not officially support ANSI escape codes (running fastfetch on them requires a third-party terminal such as ConEmu). In addition, Windows 7 lacks some APIs used by fastfetch. Fastfetch currently loads these APIs dynamically at runtime to maintain compatibility, but this adds complexity to the codebase and increases the maintenance burden.
* A CMake flag `ENABLE_WIN7_COMPAT:BOOLEAN` has been introduced (defaults to `ON` for now). If set to `OFF`, Windows 7 compatibility code is excluded, and the resulting binaries will support only Windows 10 (version 1607 and later) and Windows 11.
* The main prebuilt Windows binaries on the Release page (`fastfetch-windows-amd64.*`) are built with `ENABLE_WIN7_COMPAT=OFF`. These are the binaries used by `scoop` and `winget`. Users who need Windows 7 (or 8.x) support can download the `-win7` variant instead.
- * The `ENABLE_WIN7_COMPAT` CMake option and the `-win7` variant binaries are planned to be removed in 2.60.0.
+ * ~~The `ENABLE_WIN7_COMPAT` CMake option and the `-win7` variant binaries are planned to be removed in 2.60.0~~.
Features:
* Supports COSMIC DE version detection (DE, Linux)
diff --git a/CMakeLists.txt b/CMakeLists.txt
index 725b2b668d..063457b059 100644
--- a/CMakeLists.txt
+++ b/CMakeLists.txt
@@ -1,7 +1,7 @@
cmake_minimum_required(VERSION 3.12.0) # target_link_libraries with OBJECT libs & project homepage url
project(fastfetch
- VERSION 2.59.0
+ VERSION 2.60.0
LANGUAGES C
DESCRIPTION "Fast neofetch-like system information tool"
HOMEPAGE_URL "https://github.com/fastfetch-cli/fastfetch"
@@ -107,7 +107,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" ON)
+ option(ENABLE_WIN7_COMPAT "Enable Windows 7 compatibility" OFF)
endif()
if(APPLE)
option(ENABLE_APPLE_MEMSIZE_USABLE "Use usable memory size as total memory size in Memory module, to match other systems" OFF)
@@ -398,7 +398,6 @@ set(LIBFASTFETCH_SRC
src/common/impl/base64.c
src/common/impl/FFlist.c
src/common/impl/FFstrbuf.c
- src/common/impl/kmod.c
src/common/impl/path.c
src/common/impl/FFPlatform.c
src/common/impl/smbiosHelper.c
@@ -523,6 +522,7 @@ if(LINUX)
src/common/impl/processing_linux.c
src/common/impl/FFPlatform_unix.c
src/common/impl/binary_linux.c
+ src/common/impl/kmod_linux.c
src/detection/battery/battery_linux.c
src/detection/bios/bios_linux.c
src/detection/board/board_linux.c
@@ -607,6 +607,7 @@ elseif(ANDROID)
src/common/impl/processing_linux.c
src/common/impl/FFPlatform_unix.c
src/common/impl/binary_linux.c
+ src/common/impl/kmod_linux.c
src/detection/battery/battery_android.c
src/detection/bios/bios_android.c
src/detection/bluetooth/bluetooth_nosupport.c
@@ -681,6 +682,7 @@ elseif(FreeBSD)
src/common/impl/sysctl.c
src/common/impl/FFPlatform_unix.c
src/common/impl/binary_linux.c
+ src/common/impl/kmod_bsd.c
src/detection/battery/battery_bsd.c
src/detection/bios/bios_bsd.c
src/detection/bluetoothradio/bluetoothradio_nosupport.c
@@ -774,6 +776,7 @@ elseif(NetBSD)
src/common/impl/sysctl.c
src/common/impl/FFPlatform_unix.c
src/common/impl/binary_linux.c
+ src/common/impl/kmod_nbsd.c
src/detection/battery/battery_nbsd.c
src/detection/bios/bios_nbsd.c
src/detection/bluetooth/bluetooth_bsd.c
@@ -857,6 +860,7 @@ elseif(OpenBSD)
src/common/impl/FFPlatform_unix.c
src/common/impl/binary_linux.c
src/common/impl/smbiosHelper.c
+ src/common/impl/kmod_nosupport.c
src/detection/battery/battery_obsd.c
src/detection/bios/bios_windows.c
src/detection/bluetooth/bluetooth_nosupport.c
@@ -938,6 +942,7 @@ elseif(APPLE)
src/common/impl/sysctl.c
src/common/impl/FFPlatform_unix.c
src/common/impl/binary_apple.c
+ src/common/impl/kmod_apple.c
src/common/apple/cf_helpers.c
src/common/apple/osascript.m
src/common/apple/smc_temps.c
@@ -1009,11 +1014,13 @@ elseif(WIN32)
src/common/impl/FFPlatform_windows.c
src/common/impl/binary_windows.c
src/common/impl/debug_windows.c
+ src/common/impl/kmod_windows.c
src/common/windows/getline.c
src/common/windows/com.cpp
src/common/windows/registry.c
src/common/windows/unicode.c
src/common/windows/wmi.cpp
+ src/common/windows/variant.cpp
src/common/windows/version.c
src/detection/battery/battery_windows.c
src/detection/bios/bios_windows.c
@@ -1053,7 +1060,7 @@ elseif(WIN32)
src/detection/physicalmemory/physicalmemory_linux.c
src/detection/netio/netio_windows.c
src/detection/opengl/opengl_windows.c
- src/detection/os/os_windows.cpp
+ src/detection/os/os_windows.c
src/detection/packages/packages_windows.c
src/detection/poweradapter/poweradapter_nosupport.c
src/detection/processes/processes_windows.c
@@ -1068,7 +1075,7 @@ elseif(WIN32)
src/detection/users/users_windows.c
src/detection/wallpaper/wallpaper_windows.c
src/detection/wifi/wifi_windows.c
- src/detection/wm/wm_windows.cpp
+ src/detection/wm/wm_windows.c
src/detection/de/de_nosupport.c
src/detection/wmtheme/wmtheme_windows.c
src/detection/camera/camera_windows.cpp
@@ -1082,6 +1089,7 @@ elseif(SunOS)
src/common/impl/processing_linux.c
src/common/impl/FFPlatform_unix.c
src/common/impl/binary_linux.c
+ src/common/impl/kmod_sunos.c
src/detection/battery/battery_nosupport.c
src/detection/bios/bios_windows.c
src/detection/board/board_windows.c
@@ -1163,6 +1171,7 @@ elseif(Haiku)
src/common/impl/processing_linux.c
src/common/impl/FFPlatform_unix.c
src/common/impl/binary_linux.c
+ src/common/impl/kmod_nosupport.c
src/common/haiku/version.cpp
src/detection/battery/battery_haiku.c
src/detection/bios/bios_windows.c
@@ -1232,6 +1241,7 @@ elseif(GNU)
src/common/impl/processing_linux.c
src/common/impl/FFPlatform_unix.c
src/common/impl/binary_linux.c
+ src/common/impl/kmod_nosupport.c
src/detection/battery/battery_nosupport.c
src/detection/bios/bios_nosupport.c
src/detection/board/board_nosupport.c
@@ -1715,7 +1725,6 @@ elseif(APPLE)
)
elseif(WIN32)
target_link_libraries(libfastfetch
- PRIVATE "dwmapi"
PRIVATE "gdi32"
PRIVATE "iphlpapi"
PRIVATE "ole32"
@@ -1725,9 +1734,8 @@ elseif(WIN32)
PRIVATE "version"
PRIVATE "hid"
PRIVATE "wtsapi32"
- PRIVATE "imagehlp"
PRIVATE "cfgmgr32"
- PRIVATE "propsys"
+ PRIVATE "winbrand"
PRIVATE "secur32"
)
if(NOT ENABLE_WIN7_COMPAT)
diff --git a/README.md b/README.md
index 7fc0a17051..23143a2f1e 100644
--- a/README.md
+++ b/README.md
@@ -13,7 +13,7 @@
[](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, illumos (SunOS), and 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 7+, Android, FreeBSD, OpenBSD, NetBSD, DragonFly, Haiku and SunOS (illumos, Solaris).
diff --git a/doc/json_schema.json b/doc/json_schema.json
index dac6715f52..8627d3223c 100644
--- a/doc/json_schema.json
+++ b/doc/json_schema.json
@@ -442,7 +442,7 @@
"type": "string"
},
"packagesFormat": {
- "description": "Output format of the module `Packages`. See Wiki for formatting syntax\n 1. {all}: Number of all packages\n 2. {pacman}: Number of pacman packages\n 3. {pacman-branch}: Pacman branch on manjaro\n 4. {dpkg}: Number of dpkg packages\n 5. {rpm}: Number of rpm packages\n 6. {emerge}: Number of emerge packages\n 7. {eopkg}: Number of eopkg packages\n 8. {xbps}: Number of xbps packages\n 9. {nix-system}: Number of nix-system packages\n 10. {nix-user}: Number of nix-user packages\n 11. {nix-default}: Number of nix-default packages\n 12. {apk}: Number of apk packages\n 13. {pkg}: Number of pkg packages\n 14. {flatpak-system}: Number of flatpak-system app packages\n 15. {flatpak-user}: Number of flatpak-user app packages\n 16. {snap}: Number of snap packages\n 17. {brew}: Number of brew packages\n 18. {brew-cask}: Number of brew-cask packages\n 19. {macports}: Number of macports packages\n 20. {scoop-user}: Number of scoop-user packages\n 21. {scoop-global}: Number of scoop-global packages\n 22. {choco}: Number of choco packages\n 23. {pkgtool}: Number of pkgtool packages\n 24. {paludis}: Number of paludis packages\n 25. {winget}: Number of winget packages\n 26. {opkg}: Number of opkg packages\n 27. {am-system}: Number of am-system packages\n 28. {sorcery}: Number of sorcery packages\n 29. {lpkg}: Number of lpkg packages\n 30. {lpkgbuild}: Number of lpkgbuild packages\n 31. {guix-system}: Number of guix-system packages\n 32. {guix-user}: Number of guix-user packages\n 33. {guix-home}: Number of guix-home packages\n 34. {linglong}: Number of linglong packages\n 35. {pacstall}: Number of pacstall packages\n 36. {mport}: Number of mport packages\n 37. {am-user}: Number of am-user (aka appman) packages\n 38. {pkgsrc}: Number of pkgsrc packages\n 39. {hpkg-system}: Number of hpkg-system packages\n 40. {hpkg-user}: Number of hpkg-user packages\n 41. {pisi}: Number of pisi packages\n 42. {soar}: Number of soar packages\n 43. {kiss}: Number of kiss packages\n 44. {nix-all}: Total number of all nix packages\n 45. {flatpak-all}: Total number of all flatpak app packages\n 46. {brew-all}: Total number of all brew packages\n 47. {guix-all}: Total number of all guix packages\n 48. {hpkg-all}: Total number of all hpkg packages",
+ "description": "Output format of the module `Packages`. See Wiki for formatting syntax\n 1. {all}: Number of all packages\n 2. {pacman}: Number of pacman packages\n 3. {pacman-branch}: Pacman branch on manjaro\n 4. {dpkg}: Number of dpkg packages\n 5. {rpm}: Number of rpm packages\n 6. {emerge}: Number of emerge packages\n 7. {eopkg}: Number of eopkg packages\n 8. {xbps}: Number of xbps packages\n 9. {nix-system}: Number of nix-system packages\n 10. {nix-user}: Number of nix-user packages\n 11. {nix-default}: Number of nix-default packages\n 12. {apk}: Number of apk packages\n 13. {pkg}: Number of pkg packages\n 14. {flatpak-system}: Number of flatpak-system app packages\n 15. {flatpak-user}: Number of flatpak-user app packages\n 16. {snap}: Number of snap packages\n 17. {brew}: Number of brew packages\n 18. {brew-cask}: Number of brew-cask packages\n 19. {macports}: Number of macports packages\n 20. {scoop-user}: Number of scoop-user packages\n 21. {scoop-global}: Number of scoop-global packages\n 22. {choco}: Number of choco packages\n 23. {pkgtool}: Number of pkgtool packages\n 24. {paludis}: Number of paludis packages\n 25. {winget}: Number of winget packages\n 26. {opkg}: Number of opkg packages\n 27. {am-system}: Number of am-system packages\n 28. {sorcery}: Number of sorcery packages\n 29. {lpkg}: Number of lpkg packages\n 30. {lpkgbuild}: Number of lpkgbuild packages\n 31. {guix-system}: Number of guix-system packages\n 32. {guix-user}: Number of guix-user packages\n 33. {guix-home}: Number of guix-home packages\n 34. {linglong}: Number of linglong packages\n 35. {pacstall}: Number of pacstall packages\n 36. {mport}: Number of mport packages\n 37. {am-user}: Number of am-user (aka appman) packages\n 38. {pkgsrc}: Number of pkgsrc packages\n 39. {hpkg-system}: Number of hpkg-system packages\n 40. {hpkg-user}: Number of hpkg-user packages\n 41. {pisi}: Number of pisi packages\n 42. {soar}: Number of soar packages\n 43. {kiss}: Number of kiss packages\n 44. {moss}: Number of moss packages\n 45. {nix-all}: Total number of all nix packages\n 46. {flatpak-all}: Total number of all flatpak app packages\n 47. {brew-all}: Total number of all brew packages\n 48. {guix-all}: Total number of all guix packages\n 49. {hpkg-all}: Total number of all hpkg packages",
"type": "string"
},
"physicaldiskFormat": {
@@ -498,7 +498,7 @@
"type": "string"
},
"titleFormat": {
- "description": "Output format of the module `Title`. See Wiki for formatting syntax\n 1. {user-name}: User name\n 2. {host-name}: Host name\n 3. {home-dir}: Home directory\n 4. {exe-path}: Executable path of current process\n 5. {user-shell}: User's default shell\n 6. {user-name-colored}: User name (colored)\n 7. {at-symbol-colored}: @ symbol (colored)\n 8. {host-name-colored}: Host name (colored)\n 9. {full-user-name}: Full user name\n 10. {user-id}: UID (*nix) / SID (Windows)\n 11. {pid}: PID of current process",
+ "description": "Output format of the module `Title`. See Wiki for formatting syntax\n 1. {user-name}: User name\n 2. {host-name}: Host name\n 3. {home-dir}: Home directory\n 4. {exe-path}: Executable path of current process\n 5. {user-shell}: User's default shell\n 6. {user-name-colored}: User name (colored)\n 7. {at-symbol-colored}: @ symbol (colored)\n 8. {host-name-colored}: Host name (colored)\n 9. {full-user-name}: Full user name\n 10. {user-id}: UID (*nix) / SID (Windows)\n 11. {pid}: PID of current process\n 12. {cwd}: CWD with home dir replaced by `~`",
"type": "string"
},
"themeFormat": {
@@ -1521,7 +1521,7 @@
"description": "Print battery capacity, status, etc"
},
"useSetupApi": {
- "description": "Set if `SetupAPI` should be used on Windows to detect battery info, which supports multi batteries, but slower. Windows only",
+ "description": "Set if `CM API` should be used on Windows to detect battery info, which supports multi batteries, but slower. Windows only",
"type": "boolean",
"default": false
},
@@ -2518,11 +2518,6 @@
"const": "de",
"description": "Print desktop environment name"
},
- "slowVersionDetection": {
- "type": "boolean",
- "description": "Set if DE version should be detected with slow operations.\nShould be unnecessary for most cases.\nThis is only used as a fallback method. Please file a bug report if you encounter any issues.",
- "default": false
- },
"key": {
"$ref": "#/$defs/key"
},
@@ -4487,7 +4482,7 @@
"detectPlugin": {
"description": "Set if window manager plugin should be detected on supported platforms",
"type": "boolean",
- "default": false
+ "default": true
},
"key": {
"$ref": "#/$defs/key"
diff --git a/presets/examples/25.jsonc b/presets/examples/25.jsonc
index c00265716a..ddb3fdf2da 100644
--- a/presets/examples/25.jsonc
+++ b/presets/examples/25.jsonc
@@ -9,25 +9,26 @@
// Constants are reusable strings referenced by {$1}, {$2}, etc.
// These contain ANSI escape codes for cursor positioning
"constants": [
- "──────────────────────────────────────────────", // {$1} - horizontal line for borders
+ "──────────────────────────────────────────────", // {$1} - horizontal line for inner borders
"\u001b[47D", // {$2} - move cursor left 47 columns
"\u001b[47C", // {$3} - move cursor right 47 columns
- "\u001b[46C" // {$4} - move cursor right 46 columns
+ "\u001b[46C", // {$4} - move cursor right 46 columns
+ "══════════════════════════════════════════════" // {$5} - horizontal line for outer borders
],
"brightColor": false
},
"modules": [
{
"type": "version",
- "key": "┌───────────────┬─{$1}┐\u001b[41D",
+ "key": "╔═══════════════╦═{$5}╗\u001b[41D",
"format": "\u001b[1m{#keys} {1} - {2} "
},
{
"type": "os",
// Key format breakdown for OS module:
- // "│ {icon} \u001b[s{sysname}\u001b[u\u001b[10C│{$3}│{$2}"
+ // "║ {icon} \u001b[s{sysname}\u001b[u\u001b[10C│{$3}║{$2}"
//
- // │ - Left border of key block
+ // ║ - Left border of key block
// {icon} - OS icon (defined internally by fastfetch)
// \u001b[s - ANSI escape: save cursor position (ESC[s)
// {sysname} - Format variable: system name (e.g., "Linux", "Darwin")
@@ -36,28 +37,28 @@
// \u001b[10C - ANSI escape: move cursor right 10 columns (ESC[10C)
// │ - Right border of key block (always 10 columns from left border)
// {$3} - Reference to constants[2]: move cursor right 47 columns
- // │ - Right border of value block
+ // ║ - Right border of value block
// {$2} - Reference to constants[1]: move cursor left 47 columns
//
// This creates a fixed-width layout where the key block is exactly 10 columns wide,
// regardless of the actual content length. The cursor manipulation ensures proper
// alignment for the table-like structure.
- "key": "│ {icon} \u001b[s{sysname}\u001b[u\u001b[10C│{$3}│{$2}"
+ "key": "║ {icon} \u001b[s{sysname}\u001b[u\u001b[10C║{$3}║{$2}"
},
{
"type": "datetime",
- "key": "│ {icon} Fetched │{$3}│{$2}",
+ "key": "║ {icon} Fetched ║{$3}║{$2}",
"format": "{year}-{month-pretty}-{day-pretty} {hour-pretty}:{minute-pretty}:{second-pretty} {timezone-name}"
},
{
"type": "locale",
- "key": "│ {icon} Locale │{$3}│{$2}"
+ "key": "║ {icon} Locale ║{$3}║{$2}"
},
// Hardware section with cyan color theme
{
"type": "custom",
- "key": "│{#cyan}┌──────────────┬{$1}┐{#keys}│\u001b[37D",
+ "key": "║{#cyan}┌──────────────┬{$1}┐{#keys}║\u001b[37D",
"format": "{#bright_cyan} Hardware "
},
{
@@ -66,113 +67,113 @@
// │{#cyan}│ - Left border with cyan color
// {icon} - Chassis icon
// Chassis - Fixed label text
- // │{$4}│{#keys}│{$2} - Positioning and borders for value area
- "key": "│{#cyan}│ {icon} Chassis │{$4}│{#keys}│{$2}"
+ // │{$4}│{#keys}║{$2} - Positioning and borders for value area
+ "key": "║{#cyan}│ {icon} Chassis │{$4}│{#keys}║{$2}"
},
{
"type": "memory",
- "key": "│{#cyan}│ {icon} RAM │{$4}│{#keys}│{$2}"
+ "key": "║{#cyan}│ {icon} RAM │{$4}│{#keys}║{$2}"
},
{
"type": "swap",
- "key": "│{#cyan}│ {icon} SWAP │{$4}│{#keys}│{$2}"
+ "key": "║{#cyan}│ {icon} SWAP │{$4}│{#keys}║{$2}"
},
{
"type": "cpu",
- "key": "│{#cyan}│ {icon} CPU │{$4}│{#keys}│{$2}",
+ "key": "║{#cyan}│ {icon} CPU │{$4}│{#keys}║{$2}",
"showPeCoreCount": true
},
{
"type": "gpu",
- "key": "│{#cyan}│ {icon} GPU │{$4}│{#keys}│{$2}"
+ "key": "║{#cyan}│ {icon} GPU │{$4}│{#keys}║{$2}"
},
{
"type": "disk",
- "key": "│{#cyan}│ {icon} Disk │{$4}│{#keys}│{$2}",
+ "key": "║{#cyan}│ {icon} Disk │{$4}│{#keys}║{$2}",
"format": "{size-used} \/ {size-total} ({size-percentage}) - {filesystem}",
},
{
"type": "battery",
- "key": "│{#cyan}│ {icon} Battery │{$4}│{#keys}│{$2}"
+ "key": "║{#cyan}│ {icon} Battery │{$4}│{#keys}║{$2}"
},
{
"type": "custom",
- "key": "│{#cyan}└──────────────┴{$1}┘{#keys}│",
+ "key": "║{#cyan}└──────────────┴{$1}┘{#keys}║",
"format": ""
},
// Desktop section with green color theme
{
"type": "custom",
- "key": "│{#green}┌──────────────┬{$1}┐{#keys}│\u001b[37D",
+ "key": "║{#green}┌──────────────┬{$1}┐{#keys}║\u001b[37D",
"format": "{#bright_green} Desktop "
},
{
"type": "de",
- "key": "│{#green}│ {icon} Desktop │{$4}│{#keys}│{$2}"
+ "key": "║{#green}│ {icon} Desktop │{$4}│{#keys}║{$2}"
},
{
"type": "wm",
- "key": "│{#green}│ {icon} Session │{$4}│{#keys}│{$2}"
+ "key": "║{#green}│ {icon} Session │{$4}│{#keys}║{$2}"
},
{
"type": "display",
- "key": "│{#green}│ {icon} Display │{$4}│{#keys}│{$2}",
+ "key": "║{#green}│ {icon} Display │{$4}│{#keys}║{$2}",
"compactType": "original-with-refresh-rate"
},
{
"type": "gpu",
- "key": "│{#green}│ {icon} G-Driver │{$4}│{#keys}│{$2}",
+ "key": "║{#green}│ {icon} G-Driver │{$4}│{#keys}║{$2}",
"format": "{driver}"
},
{
"type": "custom",
- "key": "│{#green}└──────────────┴{$1}┘{#keys}│",
+ "key": "║{#green}└──────────────┴{$1}┘{#keys}║",
"format": ""
},
// Terminal section with yellow color theme
{
"type": "custom",
- "key": "│{#yellow}┌──────────────┬{$1}┐{#keys}│\u001b[37D",
+ "key": "║{#yellow}┌──────────────┬{$1}┐{#keys}║\u001b[37D",
"format": "{#bright_yellow} Terminal "
},
{
"type": "shell",
- "key": "│{#yellow}│ {icon} Shell │{$4}│{#keys}│{$2}"
+ "key": "║{#yellow}│ {icon} Shell │{$4}│{#keys}║{$2}"
},
{
"type": "terminal",
- "key": "│{#yellow}│ {icon} Terminal │{$4}│{#keys}│{$2}"
+ "key": "║{#yellow}│ {icon} Terminal │{$4}│{#keys}║{$2}"
},
{
"type": "terminalfont",
- "key": "│{#yellow}│ {icon} Term Font │{$4}│{#keys}│{$2}"
+ "key": "║{#yellow}│ {icon} Term Font │{$4}│{#keys}║{$2}"
},
{
"type": "terminaltheme",
- "key": "│{#yellow}│ {icon} Colors │{$4}│{#keys}│{$2}"
+ "key": "║{#yellow}│ {icon} Colors │{$4}│{#keys}║{$2}"
},
{
"type": "packages",
- "key": "│{#yellow}│ {icon} Packages │{$4}│{#keys}│{$2}"
+ "key": "║{#yellow}│ {icon} Packages │{$4}│{#keys}║{$2}"
},
{
"type": "custom",
- "key": "│{#yellow}└──────────────┴{$1}┘{#keys}│",
+ "key": "║{#yellow}└──────────────┴{$1}┘{#keys}║",
"format": ""
},
// Development section with red color theme
{
"type": "custom",
- "key": "│{#red}┌──────────────┬{$1}┐{#keys}│\u001b[39D",
+ "key": "║{#red}┌──────────────┬{$1}┐{#keys}║\u001b[39D",
"format": "{#bright_red} Development "
},
{
"type": "command",
"keyIcon": "", // Custom icon override
- "key": "│{#red}│ {icon} Rust │{$4}│{#keys}│{$2}",
+ "key": "║{#red}│ {icon} Rust │{$4}│{#keys}║{$2}",
"text": "rustc --version",
"format": "rustc {~6,13}" // Print 6th to 13th characters (version number)
},
@@ -182,7 +183,7 @@
"!system": "Windows" // Posix version
},
"keyIcon": "",
- "key": "│{#red}│ {icon} Clang │{$4}│{#keys}│{$2}",
+ "key": "║{#red}│ {icon} Clang │{$4}│{#keys}║{$2}",
"text": "clang --version | sed -n 's/.*version \\([0-9][0-9.]*\\).*/\\1/p'",
"format": "clang {}"
},
@@ -192,67 +193,67 @@
"system": "Windows" // Windows version
},
"keyIcon": "",
- "key": "│{#red}│ {icon} Clang │{$4}│{#keys}│{$2}",
+ "key": "║{#red}│ {icon} Clang │{$4}│{#keys}║{$2}",
"text": "clang --version | findstr version", // Finds the line with "version"
"format": "clang {~-6}" // Prints the last 6 characters (version number)
},
{
"type": "command",
"keyIcon": "",
- "key": "│{#red}│ {icon} NodeJS │{$4}│{#keys}│{$2}",
+ "key": "║{#red}│ {icon} NodeJS │{$4}│{#keys}║{$2}",
"text": "node --version",
"format": "node {~1}" // {~1} removes first character (v)
},
{
"type": "command",
"keyIcon": "",
- "key": "│{#red}│ {icon} Go │{$4}│{#keys}│{$2}",
+ "key": "║{#red}│ {icon} Go │{$4}│{#keys}║{$2}",
"text": "go version | cut -d' ' -f3",
"format": "go {~2}" // {~2} removes first 2 characters (go)
},
{
"type": "command",
"keyIcon": "",
- "key": "│{#red}│ {icon} Zig │{$4}│{#keys}│{$2}",
+ "key": "║{#red}│ {icon} Zig │{$4}│{#keys}║{$2}",
"text": "zig version",
"format": "zig {}"
},
{
"type": "editor",
- "key": "│{#red}│ {icon} Editor │{$4}│{#keys}│{$2}"
+ "key": "║{#red}│ {icon} Editor │{$4}│{#keys}║{$2}"
},
{
"type": "command",
"keyIcon": "",
- "key": "│{#red}│ {icon} Git │{$4}│{#keys}│{$2}",
+ "key": "║{#red}│ {icon} Git │{$4}│{#keys}║{$2}",
"text": "git version",
"format": "git {~12}"
},
{
"type": "font",
- "key": "│{#red}│ {icon} Interface │{$4}│{#keys}│{$2}"
+ "key": "║{#red}│ {icon} Interface │{$4}│{#keys}║{$2}"
},
{
"type": "custom",
- "key": "│{#red}└──────────────┴{$1}┘{#keys}│",
+ "key": "║{#red}└──────────────┴{$1}┘{#keys}║",
"format": ""
},
// Uptime section with magenta color theme
{
"type": "custom",
- "key": "│{#magenta}┌──────────────┬{$1}┐{#keys}│\u001b[36D",
+ "key": "║{#magenta}┌──────────────┬{$1}┐{#keys}║\u001b[36D",
"format": "{#bright_magenta} Uptime "
},
{
"type": "uptime",
- "key": "│{#magenta}│ {icon} Uptime │{$4}│{#keys}│{$2}"
+ "key": "║{#magenta}│ {icon} Uptime │{$4}│{#keys}║{$2}"
},
{
"type": "users",
"myselfOnly": true, // Only show current user
"keyIcon": "",
- "key": "│{#magenta}│ {icon} Login │{$4}│{#keys}│{$2}"
+ "key": "║{#magenta}│ {icon} Login │{$4}│{#keys}║{$2}"
},
{
"condition": { // Conditional module: only show on non-macOS
@@ -260,7 +261,7 @@
},
"type": "disk",
"keyIcon": "",
- "key": "│{#magenta}│ {icon} OS Age │{$4}│{#keys}│{$2}",
+ "key": "║{#magenta}│ {icon} OS Age │{$4}│{#keys}║{$2}",
"folders": "/", // Check root filesystem
"format": "{create-time:10} [{days} days]" // Show creation time and age in days
},
@@ -270,18 +271,18 @@
},
"type": "disk",
"keyIcon": "",
- "key": "│{#magenta}│ {icon} OS Age │{$4}│{#keys}│{$2}",
+ "key": "║{#magenta}│ {icon} OS Age │{$4}│{#keys}║{$2}",
"folders": "/System/Volumes/VM", // Work around for APFS on macOS
"format": "{create-time:10} [{days} days]"
},
{
"type": "custom",
- "key": "│{#magenta}└──────────────┴{$1}┘{#keys}│",
+ "key": "║{#magenta}└──────────────┴{$1}┘{#keys}║",
"format": ""
},
{
"type": "custom",
- "key": "└─────────────────{$1}┘", // Bottom border of the entire layout
+ "key": "╚═════════════════{$5}╝", // Bottom border of the entire layout
"format": ""
},
diff --git a/presets/examples/32.jsonc b/presets/examples/32.jsonc
new file mode 100644
index 0000000000..ff69333b2b
--- /dev/null
+++ b/presets/examples/32.jsonc
@@ -0,0 +1,57 @@
+// Inspired by microfetch
+{
+ "$schema": "https://github.com/fastfetch-cli/fastfetch/raw/dev/doc/json_schema.json",
+ "logo": {
+ "type": "small"
+ },
+ "general": {
+ "detectVersion": false
+ },
+ "display": {
+ "separator": " ",
+ "brightColor": false,
+ "key": {
+ "type": "both-2"
+ }
+ },
+ "modules": [
+ {
+ "type": "title",
+ "format": "{user-name-colored}{#light_red}@{host-name-colored} {#}{cwd}"
+ },
+ {
+ "type": "os",
+ "key": "System "
+ },
+ {
+ "type": "kernel",
+ "key": "Kernel "
+ },
+ {
+ "type": "shell",
+ "key": "Shell "
+ },
+ {
+ "type": "uptime",
+ "key": "Uptime "
+ },
+ {
+ "type": "wm",
+ "key": "Desktop "
+ },
+ {
+ "type": "memory",
+ "key": "Memory "
+ },
+ {
+ "type": "disk",
+ "key": "Storage (/) ",
+ "folders": "/"
+ },
+ {
+ "type": "colors",
+ "key": "Colors ",
+ "symbol": "circle"
+ }
+ ]
+}
diff --git a/src/common/FFPlatform.h b/src/common/FFPlatform.h
index c714b5dda8..75e81713c3 100644
--- a/src/common/FFPlatform.h
+++ b/src/common/FFPlatform.h
@@ -19,6 +19,7 @@ typedef struct FFPlatform
FFlist configDirs; // List of FFstrbuf, trailing slash included
FFlist dataDirs; // List of FFstrbuf, trailing slash included
FFstrbuf exePath; // The real path of current exe (empty if unavailable)
+ FFstrbuf cwd; // Trailing slash included
uint32_t pid;
#ifndef _WIN32
diff --git a/src/common/dbus.h b/src/common/dbus.h
index ee5eb432ca..cecd744cbc 100644
--- a/src/common/dbus.h
+++ b/src/common/dbus.h
@@ -19,6 +19,7 @@ typedef struct FFDBusLibrary
FF_LIBRARY_SYMBOL(dbus_message_iter_next)
FF_LIBRARY_SYMBOL(dbus_message_unref)
FF_LIBRARY_SYMBOL(dbus_connection_send_with_reply_and_block)
+ FF_LIBRARY_SYMBOL(dbus_connection_unref)
} FFDBusLibrary;
typedef struct FFDBusData
@@ -36,10 +37,13 @@ DBusMessage* ffDBusGetProperty(FFDBusData* dbus, const char* busName, const char
bool ffDBusGetPropertyString(FFDBusData* dbus, const char* busName, const char* objectPath, const char* interface, const char* property, FFstrbuf* result);
bool ffDBusGetInt(FFDBusData* dbus, DBusMessageIter* iter, int32_t* result);
bool ffDBusGetPropertyUint(FFDBusData* dbus, const char* busName, const char* objectPath, const char* interface, const char* property, uint32_t* result);
+void ffDBusDestroyData(FFDBusData* data);
static inline DBusMessage* ffDBusGetAllProperties(FFDBusData* dbus, const char* busName, const char* objectPath, const char* interface)
{
return ffDBusGetMethodReply(dbus, busName, objectPath, "org.freedesktop.DBus.Properties", "GetAll", interface, NULL);
}
+#define FF_DBUS_AUTO_DESTROY_DATA __attribute__((__cleanup__(ffDBusDestroyData)))
+
#endif // FF_HAVE_DBUS
diff --git a/src/common/impl/FFPlatform.c b/src/common/impl/FFPlatform.c
index 0c42a649f3..f84ab5878b 100644
--- a/src/common/impl/FFPlatform.c
+++ b/src/common/impl/FFPlatform.c
@@ -10,6 +10,7 @@ void ffPlatformInit(FFPlatform* platform)
ffListInit(&platform->configDirs, sizeof(FFstrbuf));
ffListInit(&platform->dataDirs, sizeof(FFstrbuf));
ffStrbufInit(&platform->exePath);
+ ffStrbufInit(&platform->cwd);
ffStrbufInit(&platform->userName);
ffStrbufInit(&platform->fullUserName);
@@ -49,6 +50,7 @@ void ffPlatformDestroy(FFPlatform* platform)
ffStrbufDestroy(dir);
ffListDestroy(&platform->dataDirs);
ffStrbufDestroy(&platform->exePath);
+ ffStrbufDestroy(&platform->cwd);
ffStrbufDestroy(&platform->userName);
ffStrbufDestroy(&platform->hostName);
diff --git a/src/common/impl/FFPlatform_unix.c b/src/common/impl/FFPlatform_unix.c
index 985494bf9e..c93c863bde 100644
--- a/src/common/impl/FFPlatform_unix.c
+++ b/src/common/impl/FFPlatform_unix.c
@@ -1,5 +1,6 @@
#include "FFPlatform_private.h"
#include "common/FFstrbuf.h"
+#include "common/arrayUtils.h"
#include "common/stringUtils.h"
#include "common/io.h"
#include "fastfetch_config.h"
@@ -11,10 +12,14 @@
#include
#ifdef __APPLE__
- #include
+ #include
#include
-#elif defined(__FreeBSD__) || defined(__OpenBSD__) || defined(__NetBSD__)
+#elif defined(__FreeBSD__) || defined(__NetBSD__)
#include
+#elif defined(__OpenBSD__)
+ #include
+ #include
+ #include "common/path.h"
#elif defined(__HAIKU__)
#include
#include
@@ -28,7 +33,11 @@ static void getExePath(FFPlatform* platform)
if (exePathLen >= 0)
exePath[exePathLen] = '\0';
#elif defined(__APPLE__)
- int exePathLen = proc_pidpath((pid_t) platform->pid, exePath, sizeof(exePath));
+ uint32_t exePathLen = sizeof(exePath);
+ if (_NSGetExecutablePath(exePath, &exePathLen) == 0)
+ exePathLen = (uint32_t) strlen(exePath);
+ else
+ exePathLen = 0;
#elif defined(__FreeBSD__) || defined(__NetBSD__)
size_t exePathLen = sizeof(exePath);
if(sysctl(
@@ -46,7 +55,81 @@ static void getExePath(FFPlatform* platform)
else
exePathLen--; // remove terminating NUL
#elif defined(__OpenBSD__)
+ // OpenBSD doesn't have a reliable way to get the executable path.
+ // Current implementation uses argv[0], which can be easily spoofed.
+ // See #2195
size_t exePathLen = 0;
+ kvm_t* kd = kvm_openfiles(NULL, NULL, NULL, KVM_NO_FILES, NULL);
+ if (kd)
+ {
+ int kpCount;
+ struct kinfo_proc* kp = kvm_getprocs(kd, KERN_PROC_PID, (pid_t) platform->pid, sizeof(*kp), &kpCount);
+ if (kp && kpCount == 1)
+ {
+ char** argv = kvm_getargv(kd, kp, 0);
+ if (argv && argv[0])
+ {
+ char* arg0 = argv[0];
+ if (arg0[0])
+ {
+ if (strchr(arg0, '/') != NULL) // likely a path (absolute or relative)
+ {
+ exePathLen = strlen(arg0);
+ if (exePathLen < ARRAY_SIZE(exePath))
+ {
+ memcpy(exePath, arg0, exePathLen);
+ exePath[exePathLen] = '\0';
+ }
+ else
+ exePathLen = 0;
+ }
+ else
+ {
+ FF_STRBUF_AUTO_DESTROY tmpPath = ffStrbufCreate();
+ if (ffFindExecutableInPath(arg0, &tmpPath) == NULL && tmpPath.length < ARRAY_SIZE(exePath))
+ {
+ memcpy(exePath, tmpPath.chars, tmpPath.length + 1);
+ exePathLen = tmpPath.length;
+ }
+ }
+
+ if (exePathLen > 0)
+ {
+ struct stat st;
+ if (stat(exePath, &st) == 0 && S_ISREG(st.st_mode))
+ {
+ int cntp;
+ struct kinfo_file* kf = kvm_getfiles(kd, KERN_FILE_BYPID, platform->pid, sizeof(*kf), &cntp);
+ if (kf)
+ {
+ int i;
+ for (i = 0; i < cntp; i++)
+ {
+ if (kf[i].fd_fd == KERN_FILE_TEXT)
+ {
+ // KERN_FILE_TEXT is the executable file, not a shared library, and should be unique in the list.
+ if (st.st_dev != (dev_t)kf[i].va_fsid || st.st_ino != (ino_t)kf[i].va_fileid)
+ i = -1;
+ break;
+ }
+ }
+ if (i < 0)
+ exePathLen = 0;
+ }
+ else
+ {
+ // If we can't get the list of open files, we can't verify that the file is actually the executable
+ // Assume it is
+ }
+ }
+ else
+ exePathLen = 0;
+ }
+ }
+ }
+ }
+ kvm_close(kd);
+ }
#elif defined(__sun)
ssize_t exePathLen = readlink("/proc/self/path/a.out", exePath, sizeof(exePath) - 1);
if (exePathLen >= 0)
@@ -224,6 +307,16 @@ static void getSysinfo(FFPlatformSysinfo* info, const struct utsname* uts)
#endif
}
+static void getCwd(FFPlatform* platform)
+{
+ char cwd[PATH_MAX];
+ if (getcwd(cwd, sizeof(cwd)) != NULL)
+ {
+ ffStrbufSetS(&platform->cwd, cwd);
+ ffStrbufEnsureEndsWithC(&platform->cwd, '/');
+ }
+}
+
void ffPlatformInitImpl(FFPlatform* platform)
{
platform->pid = (uint32_t) getpid();
@@ -235,6 +328,7 @@ void ffPlatformInitImpl(FFPlatform* platform)
memset(&uts, 0, sizeof(uts));
getExePath(platform);
+ getCwd(platform);
getHomeDir(platform, pwd);
getCacheDir(platform);
getConfigDirs(platform);
diff --git a/src/common/impl/FFPlatform_windows.c b/src/common/impl/FFPlatform_windows.c
index c09cdc2770..d502ee1ccb 100644
--- a/src/common/impl/FFPlatform_windows.c
+++ b/src/common/impl/FFPlatform_windows.c
@@ -208,10 +208,6 @@ static const char* detectWine(void)
static void getSystemReleaseAndVersion(FFPlatformSysinfo* info)
{
- RTL_OSVERSIONINFOW osVersion = { .dwOSVersionInfoSize = sizeof(osVersion) };
- if (!NT_SUCCESS(RtlGetVersion(&osVersion)))
- return;
-
FF_HKEY_AUTO_DESTROY hKey = NULL;
if(!ffRegOpenKeyForRead(HKEY_LOCAL_MACHINE, L"SOFTWARE\\Microsoft\\Windows NT\\CurrentVersion", &hKey, NULL))
return;
@@ -221,40 +217,41 @@ static void getSystemReleaseAndVersion(FFPlatformSysinfo* info)
ffStrbufAppendF(&info->release,
"%u.%u.%u.%u",
- (unsigned) osVersion.dwMajorVersion,
- (unsigned) osVersion.dwMinorVersion,
- (unsigned) osVersion.dwBuildNumber,
+ (unsigned) SharedUserData->NtMajorVersion,
+ (unsigned) SharedUserData->NtMinorVersion,
+ (unsigned) SharedUserData->NtBuildNumber,
(unsigned) ubr);
ffRegReadStrbuf(hKey, L"BuildLabEx", &info->version, NULL);
const char* wineVersion = detectWine();
if (wineVersion)
- ffStrbufSetF(&info->name, "Wine_%s", wineVersion);
- else
{
- switch (osVersion.dwPlatformId)
- {
- case VER_PLATFORM_WIN32s:
- ffStrbufSetStatic(&info->name, "WIN32s");
- break;
- case VER_PLATFORM_WIN32_WINDOWS:
- ffStrbufSetStatic(&info->name, "WIN32_WINDOWS");
- break;
- case VER_PLATFORM_WIN32_NT:
- ffStrbufSetStatic(&info->name, "WIN32_NT");
- break;
- }
+ if (instance.config.general.detectVersion)
+ ffStrbufSetF(&info->name, "Wine_%s", wineVersion);
+ else
+ ffStrbufSetStatic(&info->name, "Wine");
}
+ else
+ ffStrbufSetStatic(&info->name, "WIN32_NT");
}
-static void getSystemArchitectureAndPageSize(FFPlatformSysinfo* info)
+static void getSystemPageSize(FFPlatformSysinfo* info)
{
- SYSTEM_INFO sysInfo;
- GetNativeSystemInfo(&sysInfo);
+ SYSTEM_BASIC_INFORMATION sbi;
+ if (NT_SUCCESS(NtQuerySystemInformation(SystemBasicInformation, &sbi, sizeof(sbi), NULL)))
+ info->pageSize = sbi.PhysicalPageSize;
+ else
+ info->pageSize = 4096;
+}
- switch(sysInfo.wProcessorArchitecture)
+static void getSystemArchitecture(FFPlatformSysinfo* info)
+{
+ SYSTEM_PROCESSOR_INFORMATION spi;
+ if (NT_SUCCESS(NtQuerySystemInformation(SystemProcessorInformation, &spi, sizeof(spi), NULL)))
{
+ switch (spi.ProcessorArchitecture)
+ {
case PROCESSOR_ARCHITECTURE_AMD64:
ffStrbufSetStatic(&info->architecture, "x86_64");
break;
@@ -262,7 +259,7 @@ static void getSystemArchitectureAndPageSize(FFPlatformSysinfo* info)
ffStrbufSetStatic(&info->architecture, "ia64");
break;
case PROCESSOR_ARCHITECTURE_INTEL:
- switch (sysInfo.wProcessorLevel)
+ switch (spi.ProcessorLevel)
{
case 4:
ffStrbufSetStatic(&info->architecture, "i486");
@@ -300,15 +297,28 @@ static void getSystemArchitectureAndPageSize(FFPlatformSysinfo* info)
default:
ffStrbufSetStatic(&info->architecture, "unknown");
break;
+ }
}
+}
- info->pageSize = sysInfo.dwPageSize;
+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];
+ ffStrbufSetNWS(&platform->cwd, cwd->DosPath.Length / sizeof(WCHAR), cwd->DosPath.Buffer);
+ ffStrbufReplaceAllC(&platform->cwd, '\\', '/');
+ ffStrbufEnsureEndsWithC(&platform->cwd, '/');
}
void ffPlatformInitImpl(FFPlatform* platform)
{
platform->pid = (uint32_t) GetCurrentProcessId();
getExePath(platform);
+ getCwd(platform);
getHomeDir(platform);
getCacheDir(platform);
getConfigDirs(platform);
@@ -319,5 +329,6 @@ void ffPlatformInitImpl(FFPlatform* platform)
getUserShell(platform);
getSystemReleaseAndVersion(&platform->sysinfo);
- getSystemArchitectureAndPageSize(&platform->sysinfo);
+ getSystemArchitecture(&platform->sysinfo);
+ getSystemPageSize(&platform->sysinfo);
}
diff --git a/src/common/impl/binary_windows.c b/src/common/impl/binary_windows.c
index b6f0bb1faa..980c0d3132 100644
--- a/src/common/impl/binary_windows.c
+++ b/src/common/impl/binary_windows.c
@@ -1,10 +1,9 @@
#include "common/binary.h"
#include "common/io.h"
#include "common/stringUtils.h"
-#include "common/mallocHelper.h"
+#include "common/windows/nt.h"
#include
-#include
#include
#include
@@ -17,19 +16,33 @@
*/
const char* ffBinaryExtractStrings(const char *peFile, bool (*cb)(const char *str, uint32_t len, void *userdata), void *userdata, uint32_t minLength)
{
- // Use MapAndLoad with cleanup attribute to ensure proper unloading
- __attribute__((__cleanup__(UnMapAndLoad))) LOADED_IMAGE loadedImage = {};
- if (!MapAndLoad(peFile, NULL, &loadedImage, FALSE, TRUE))
- return "File could not be loaded";
+ FF_AUTO_CLOSE_FD HANDLE hFile = CreateFileA(peFile, GENERIC_READ, FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE,
+ NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL);
+ if (hFile == INVALID_HANDLE_VALUE)
+ return "CreateFileA() failed";
- // Iterate through all sections in the PE file
- for (ULONG i = 0; i < loadedImage.NumberOfSections; ++i)
+ FF_AUTO_CLOSE_FD HANDLE hMap = CreateFileMappingW(hFile, NULL, PAGE_READONLY, 0, 0, NULL);
+ if (!hMap)
+ return "CreateFileMappingW() failed";
+
+ void* base = MapViewOfFile(hMap, FILE_MAP_READ, 0, 0, 0);
+ if (!base)
+ return "MapViewOfFile() failed";
+
+ PIMAGE_NT_HEADERS ntHeaders = RtlImageNtHeader(base);
+ if (!ntHeaders)
+ {
+ UnmapViewOfFile(base);
+ return "RtlImageNtHeader() failed";
+ }
+
+ PIMAGE_SECTION_HEADER section = IMAGE_FIRST_SECTION(ntHeaders);
+ for (WORD i = 0; i < ntHeaders->FileHeader.NumberOfSections; ++i, ++section)
{
- PIMAGE_SECTION_HEADER section = &loadedImage.Sections[i];
// Look for initialized data sections with the name ".rdata" which typically contains string literals
if ((section->Characteristics & IMAGE_SCN_CNT_INITIALIZED_DATA) && ffStrEquals((const char*) section->Name, ".rdata"))
{
- uint8_t *data = (uint8_t *) loadedImage.MappedAddress + section->PointerToRawData;
+ uint8_t *data = (uint8_t *) base + section->PointerToRawData;
// Scan the section for string literals
for (size_t off = 0; off < section->SizeOfRawData; ++off)
@@ -48,5 +61,6 @@ const char* ffBinaryExtractStrings(const char *peFile, bool (*cb)(const char *st
}
}
+ UnmapViewOfFile(base);
return NULL;
}
diff --git a/src/common/impl/dbus.c b/src/common/impl/dbus.c
index 87aacdf85e..cf45700a99 100644
--- a/src/common/impl/dbus.c
+++ b/src/common/impl/dbus.c
@@ -19,6 +19,7 @@ static bool loadLibSymbols(FFDBusLibrary* lib)
FF_LIBRARY_LOAD_SYMBOL_PTR(dbus, lib, dbus_message_iter_next, false)
FF_LIBRARY_LOAD_SYMBOL_PTR(dbus, lib, dbus_message_unref, false)
FF_LIBRARY_LOAD_SYMBOL_PTR(dbus, lib, dbus_connection_send_with_reply_and_block, false)
+ FF_LIBRARY_LOAD_SYMBOL_PTR(dbus, lib, dbus_connection_unref, false)
dbus = NULL; // don't auto dlclose
return true;
}
@@ -51,6 +52,15 @@ const char* ffDBusLoadData(DBusBusType busType, FFDBusData* data)
return NULL;
}
+void ffDBusDestroyData(FFDBusData* data)
+{
+ if (data->connection != NULL)
+ {
+ data->lib->ffdbus_connection_unref(data->connection);
+ data->connection = NULL;
+ }
+}
+
bool ffDBusGetString(FFDBusData* dbus, DBusMessageIter* iter, FFstrbuf* result)
{
int argType = dbus->lib->ffdbus_message_iter_get_arg_type(iter);
diff --git a/src/common/impl/format.c b/src/common/impl/format.c
index 469aceed48..7a5a14a79e 100644
--- a/src/common/impl/format.c
+++ b/src/common/impl/format.c
@@ -87,7 +87,7 @@ static uint32_t getArgumentIndex(const char* placeholderValue, uint32_t numArgs,
for (uint32_t i = 0; i < numArgs; ++i)
{
const FFformatarg* arg = &arguments[i];
- if (arg->name && strcasecmp(placeholderValue, arg->name) == 0)
+ if (arg->name && ffStrEqualsIgnCase(placeholderValue, arg->name))
return i + 1;
}
}
@@ -204,7 +204,7 @@ void ffParseFormatString(FFstrbuf* buffer, const FFstrbuf* formatstr, uint32_t n
uint32_t index = getArgumentIndex(placeholderValue.chars, numArgs, arguments);
// testing for an invalid index
- if (index > numArgs)
+ if (index > numArgs || index < 1)
{
appendInvalidPlaceholder(buffer, "{?", &placeholderValue, i, formatstr->length);
continue;
@@ -230,7 +230,7 @@ void ffParseFormatString(FFstrbuf* buffer, const FFstrbuf* formatstr, uint32_t n
uint32_t index = getArgumentIndex(placeholderValue.chars, numArgs, arguments);
// testing for an invalid index
- if (index > numArgs)
+ if (index > numArgs || index < 1)
{
appendInvalidPlaceholder(buffer, "{/", &placeholderValue, i, formatstr->length);
continue;
diff --git a/src/common/impl/kmod.c b/src/common/impl/kmod.c
deleted file mode 100644
index 145c6ab78b..0000000000
--- a/src/common/impl/kmod.c
+++ /dev/null
@@ -1,95 +0,0 @@
-#include "common/kmod.h"
-
-#if __linux__
-#include "common/io.h"
-
-bool ffKmodLoaded(const char* modName)
-{
- static FFstrbuf modules;
- if (modules.chars == NULL)
- {
- ffStrbufInitS(&modules, "\n");
- ffAppendFileBuffer("/proc/modules", &modules);
- }
-
- if (modules.length == 0) return false;
-
- uint32_t len = (uint32_t) strlen(modName);
- if (len > 250) return false;
-
- char temp[256];
- temp[0] = '\n';
- memcpy(temp + 1, modName, len);
- temp[1 + len] = ' ';
- return memmem(modules.chars, modules.length, temp, len + 2) != NULL;
-}
-#elif __FreeBSD__
-#include
-#include
-
-bool ffKmodLoaded(const char* modName)
-{
- return modfind(modName) >= 0;
-}
-#elif __NetBSD__
-#include "common/stringUtils.h"
-
-#include
-#include
-
-typedef struct __attribute__((__packed__)) FFNbsdModList
-{
- int len;
- modstat_t mods[];
-} FFNbsdModList;
-
-bool ffKmodLoaded(const char* modName)
-{
- static FFNbsdModList* list = NULL;
-
- if (list == NULL)
- {
- struct iovec iov = {};
-
- for (size_t len = 8192;; len = iov.iov_len)
- {
- iov.iov_len = len;
- iov.iov_base = realloc(iov.iov_base, len);
- if (modctl(MODCTL_STAT, &iov) < 0)
- {
- free(iov.iov_base);
- return true; // ignore errors
- }
-
- if (len >= iov.iov_len) break;
- }
- list = (FFNbsdModList*) iov.iov_base;
- }
-
- for (int i = 0; i < list->len; i++)
- {
- if (ffStrEquals(list->mods[i].ms_name, modName))
- return true;
- }
-
- return false;
-}
-#elif __APPLE__
-#include "common/apple/cf_helpers.h"
-#include
-#include
-
-bool ffKmodLoaded(const char* modName)
-{
- FF_CFTYPE_AUTO_RELEASE CFStringRef name = CFStringCreateWithCString(kCFAllocatorDefault, modName, kCFStringEncodingUTF8);
- FF_CFTYPE_AUTO_RELEASE CFArrayRef identifiers = CFArrayCreate(kCFAllocatorDefault, (const void**) &name, 1, &kCFTypeArrayCallBacks);
- FF_CFTYPE_AUTO_RELEASE CFArrayRef keys = CFArrayCreate(kCFAllocatorDefault, NULL, 0, NULL);
- FF_CFTYPE_AUTO_RELEASE CFDictionaryRef kextInfo = KextManagerCopyLoadedKextInfo(identifiers, keys);
- return CFDictionaryContainsKey(kextInfo, name);
-}
-#else
-bool ffKmodLoaded(FF_MAYBE_UNUSED const char* modName)
-{
- return true; // Don't generate kernel module related errors
-}
-#endif
diff --git a/src/common/impl/kmod_apple.c b/src/common/impl/kmod_apple.c
new file mode 100644
index 0000000000..eb28faee69
--- /dev/null
+++ b/src/common/impl/kmod_apple.c
@@ -0,0 +1,13 @@
+#include "common/kmod.h"
+#include "common/apple/cf_helpers.h"
+#include
+#include
+
+bool ffKmodLoaded(const char* modName)
+{
+ FF_CFTYPE_AUTO_RELEASE CFStringRef name = CFStringCreateWithCString(kCFAllocatorDefault, modName, kCFStringEncodingUTF8);
+ FF_CFTYPE_AUTO_RELEASE CFArrayRef identifiers = CFArrayCreate(kCFAllocatorDefault, (const void**) &name, 1, &kCFTypeArrayCallBacks);
+ FF_CFTYPE_AUTO_RELEASE CFArrayRef keys = CFArrayCreate(kCFAllocatorDefault, NULL, 0, NULL);
+ FF_CFTYPE_AUTO_RELEASE CFDictionaryRef kextInfo = KextManagerCopyLoadedKextInfo(identifiers, keys);
+ return CFDictionaryContainsKey(kextInfo, name);
+}
diff --git a/src/common/impl/kmod_bsd.c b/src/common/impl/kmod_bsd.c
new file mode 100644
index 0000000000..99ebafda7b
--- /dev/null
+++ b/src/common/impl/kmod_bsd.c
@@ -0,0 +1,8 @@
+#include "common/kmod.h"
+#include
+#include
+
+bool ffKmodLoaded(const char* modName)
+{
+ return modfind(modName) >= 0;
+}
diff --git a/src/common/impl/kmod_linux.c b/src/common/impl/kmod_linux.c
new file mode 100644
index 0000000000..940093b5db
--- /dev/null
+++ b/src/common/impl/kmod_linux.c
@@ -0,0 +1,23 @@
+#include "common/kmod.h"
+#include "common/io.h"
+
+bool ffKmodLoaded(const char* modName)
+{
+ static FFstrbuf modules;
+ if (modules.chars == NULL)
+ {
+ ffStrbufInitS(&modules, "\n");
+ ffAppendFileBuffer("/proc/modules", &modules);
+ }
+
+ if (modules.length == 0) return false;
+
+ uint32_t len = (uint32_t) strlen(modName);
+ if (len > 250) return false;
+
+ char temp[256];
+ temp[0] = '\n';
+ memcpy(temp + 1, modName, len);
+ temp[1 + len] = ' ';
+ return memmem(modules.chars, modules.length, temp, len + 2) != NULL;
+}
diff --git a/src/common/impl/kmod_nbsd.c b/src/common/impl/kmod_nbsd.c
new file mode 100644
index 0000000000..f4e78eed8f
--- /dev/null
+++ b/src/common/impl/kmod_nbsd.c
@@ -0,0 +1,43 @@
+#include "common/kmod.h"
+#include "common/stringUtils.h"
+
+#include
+#include
+
+typedef struct __attribute__((__packed__)) FFNbsdModList
+{
+ int len;
+ modstat_t mods[];
+} FFNbsdModList;
+
+bool ffKmodLoaded(const char* modName)
+{
+ static FFNbsdModList* list = NULL;
+
+ if (list == NULL)
+ {
+ struct iovec iov = {};
+
+ for (size_t len = 8192;; len = iov.iov_len)
+ {
+ iov.iov_len = len;
+ iov.iov_base = realloc(iov.iov_base, len);
+ if (modctl(MODCTL_STAT, &iov) < 0)
+ {
+ free(iov.iov_base);
+ return true; // ignore errors
+ }
+
+ if (len >= iov.iov_len) break;
+ }
+ list = (FFNbsdModList*) iov.iov_base;
+ }
+
+ for (int i = 0; i < list->len; i++)
+ {
+ if (ffStrEquals(list->mods[i].ms_name, modName))
+ return true;
+ }
+
+ return false;
+}
diff --git a/src/common/impl/kmod_nosupport.c b/src/common/impl/kmod_nosupport.c
new file mode 100644
index 0000000000..1ae3b0b33f
--- /dev/null
+++ b/src/common/impl/kmod_nosupport.c
@@ -0,0 +1,6 @@
+#include "common/kmod.h"
+
+bool ffKmodLoaded(FF_MAYBE_UNUSED const char* modName)
+{
+ return true; // Don't generate kernel module related errors
+}
diff --git a/src/common/impl/kmod_sunos.c b/src/common/impl/kmod_sunos.c
new file mode 100644
index 0000000000..e6c403c82f
--- /dev/null
+++ b/src/common/impl/kmod_sunos.c
@@ -0,0 +1,24 @@
+#include "common/kmod.h"
+#include "common/stringUtils.h"
+
+#include
+#include
+
+bool ffKmodLoaded(const char* modName)
+{
+ struct modinfo modinfo = {
+ .mi_id = -1,
+ .mi_nextid = -1,
+ .mi_info = MI_INFO_ALL,
+ };
+
+ for (int id = -1; modctl(MODINFO, id, &modinfo) == 0; id = modinfo.mi_id)
+ {
+ modinfo.mi_name[MODMAXNAMELEN - 1] = '\0';
+
+ if (ffStrEquals(modinfo.mi_name, modName))
+ return true;
+ }
+
+ return !(errno == EINVAL || errno == ENOENT);
+}
diff --git a/src/common/impl/kmod_windows.c b/src/common/impl/kmod_windows.c
new file mode 100644
index 0000000000..3eb275760c
--- /dev/null
+++ b/src/common/impl/kmod_windows.c
@@ -0,0 +1,27 @@
+#include "common/kmod.h"
+#include "common/windows/nt.h"
+#include "common/mallocHelper.h"
+#include "common/stringUtils.h"
+
+bool ffKmodLoaded(const char* modName)
+{
+ ULONG bufferSize = 0;
+ NtQuerySystemInformation(SystemModuleInformation, NULL, 0, &bufferSize);
+ if (bufferSize == 0)
+ return true; // ignore errors
+
+ FF_AUTO_FREE RTL_PROCESS_MODULES* buffer = malloc(bufferSize);
+
+ if (!NT_SUCCESS(NtQuerySystemInformation(SystemModuleInformation, buffer, bufferSize, &bufferSize)))
+ return true; // ignore errors
+
+ for (ULONG i = 0; i < buffer->NumberOfModules; i++)
+ {
+ const char* name = (const char*) buffer->Modules[i].FullPathName + buffer->Modules[i].OffsetToFileName;
+
+ if (ffStrEqualsIgnCase(name, modName))
+ return true;
+ }
+
+ return false;
+}
diff --git a/src/common/impl/percent.c b/src/common/impl/percent.c
index 1ab0c1e570..a367bce57b 100644
--- a/src/common/impl/percent.c
+++ b/src/common/impl/percent.c
@@ -261,6 +261,8 @@ bool ffPercentParseCommandOptions(const char* key, const char* subkey, const cha
bool ffPercentParseJsonObject(yyjson_val* key, yyjson_val* value, FFPercentageModuleConfig* config)
{
+ assert(key);
+
if (!unsafe_yyjson_equals_str(key, "percent"))
return false;
diff --git a/src/common/impl/processing_windows.c b/src/common/impl/processing_windows.c
index f0dc096cd1..0faf27b41b 100644
--- a/src/common/impl/processing_windows.c
+++ b/src/common/impl/processing_windows.c
@@ -7,7 +7,6 @@
#include
#include
#include
-#include
enum { FF_PIPE_BUFSIZ = 8192 };
@@ -220,9 +219,6 @@ bool ffProcessGetInfoWindows(uint32_t pid, uint32_t* ppid, FFstrbuf* pname, FFst
if (hProcess == NULL)
return false;
- if (gui)
- *gui = GetGuiResources(hProcess, GR_GDIOBJECTS) > 0;
-
if(ppid)
{
PROCESS_BASIC_INFORMATION info = {};
@@ -235,26 +231,41 @@ bool ffProcessGetInfoWindows(uint32_t pid, uint32_t* ppid, FFstrbuf* pname, FFst
else
return false;
}
+
if(exe)
{
// TODO: It's possible to query the command line with `NtQueryInformationProcess(60/*ProcessCommandLineInformation*/)` since Windows 8.1
- alignas(alignof(UNICODE_STRING)) uint8_t buffer[4096];
+ alignas(UNICODE_STRING) uint8_t buffer[4096];
ULONG size;
if(NT_SUCCESS(NtQueryInformationProcess(hProcess, ProcessImageFileNameWin32, &buffer, sizeof(buffer), &size)))
{
- UNICODE_STRING* imageName = (UNICODE_STRING*)buffer;
- ffStrbufSetNWS(exe, imageName->Length / sizeof(wchar_t), imageName->Buffer);
+ UNICODE_STRING* imagePath = (UNICODE_STRING*)buffer;
+ ffStrbufSetNWS(exe, imagePath->Length / sizeof(wchar_t), imagePath->Buffer);
if (exePath) ffStrbufSet(exePath, exe);
+
+ if (pname && exeName)
+ {
+ *exeName = exe->chars + ffStrbufLastIndexC(exe, '\\') + 1;
+ ffStrbufSetS(pname, *exeName);
+ }
}
else
return false;
}
- if(pname && exeName)
+
+ if (gui)
{
- *exeName = exe->chars + ffStrbufLastIndexC(exe, '\\') + 1;
- ffStrbufSetS(pname, *exeName);
+ SECTION_IMAGE_INFORMATION info = {};
+ ULONG size;
+ if(NT_SUCCESS(NtQueryInformationProcess(hProcess, ProcessImageInformation, &info, sizeof(info), &size)))
+ {
+ assert(size == sizeof(info));
+ *gui = info.SubSystemType == IMAGE_SUBSYSTEM_WINDOWS_GUI;
+ }
+ else
+ return false;
}
return true;
diff --git a/src/common/impl/settings.c b/src/common/impl/settings.c
index bb7d820cf1..acb42195b1 100644
--- a/src/common/impl/settings.c
+++ b/src/common/impl/settings.c
@@ -201,7 +201,7 @@ FFvariant ffSettingsGetGnome(const char* dconfKey, const char* gsettingsSchemaNa
FFvariant ffSettingsGetXFConf(const char* channelName, const char* propertyName, FFvarianttype type)
{
- FFDBusData dbus;
+ FF_DBUS_AUTO_DESTROY_DATA FFDBusData dbus = {};
if (ffDBusLoadData(DBUS_BUS_SESSION, &dbus) != NULL)
return FF_VARIANT_NULL;
@@ -260,7 +260,7 @@ FFvariant ffSettingsGetXFConf(const char* channelName, const char* propertyName,
FFvariant ffSettingsGetXFConfFirstMatch(const char* channelName, const char* propertyPrefix, FFvarianttype type, void* data, FFTestXfconfPropCallback* cb)
{
- FFDBusData dbus;
+ FF_DBUS_AUTO_DESTROY_DATA FFDBusData dbus = {};
if (ffDBusLoadData(DBUS_BUS_SESSION, &dbus) != NULL)
return FF_VARIANT_NULL;
diff --git a/src/common/impl/smbiosHelper.c b/src/common/impl/smbiosHelper.c
index ba573b181b..2e7af8dd51 100644
--- a/src/common/impl/smbiosHelper.c
+++ b/src/common/impl/smbiosHelper.c
@@ -109,7 +109,7 @@ typedef struct FFSmbios20EntryPoint
uint8_t SmbiosBcdRevision;
} __attribute__((__packed__)) FFSmbios20EntryPoint;
static_assert(offsetof(FFSmbios20EntryPoint, SmbiosBcdRevision) == 0x1E,
- "FFSmbios30EntryPoint: Wrong struct alignment");
+ "FFSmbios20EntryPoint: Wrong struct alignment");
typedef struct FFSmbios30EntryPoint
{
@@ -434,7 +434,7 @@ const FFSmbiosHeaderTable* ffGetSmbiosHeaderTable()
return &table;
}
#elif defined(_WIN32)
-#include
+#include "common/windows/nt.h"
#pragma GCC diagnostic ignored "-Wmultichar"
@@ -450,36 +450,52 @@ typedef struct FFRawSmbiosData
const FFSmbiosHeaderTable* ffGetSmbiosHeaderTable()
{
- static FFRawSmbiosData* buffer;
+ static SYSTEM_FIRMWARE_TABLE_INFORMATION* buffer;
static FFSmbiosHeaderTable table;
if (!buffer)
{
FF_DEBUG("Initializing Windows SMBIOS buffer");
- const DWORD signature = 'RSMB';
+
FF_DEBUG("Querying system firmware table size with signature 'RSMB'");
- uint32_t bufSize = GetSystemFirmwareTable(signature, 0, NULL, 0);
- if (bufSize <= sizeof(FFRawSmbiosData)) {
- FF_DEBUG("Invalid firmware table size: %u (must be > %zu)",
- bufSize, sizeof(FFRawSmbiosData));
+ SYSTEM_FIRMWARE_TABLE_INFORMATION sfti = {
+ .ProviderSignature = 'RSMB',
+ .Action = SystemFirmwareTableGet,
+ };
+ ULONG bufSize = 0;
+ NtQuerySystemInformation(SystemFirmwareTableInformation, &sfti, sizeof(sfti), &bufSize);
+ if (bufSize <= sizeof(FFRawSmbiosData) + sizeof(sfti)) {
+ FF_DEBUG("Invalid firmware table size: %lu (must be > %zu)", bufSize, sizeof(FFRawSmbiosData) + sizeof(sfti));
+ return NULL;
+ }
+ if (bufSize != sfti.TableBufferLength + (ULONG) sizeof(sfti)) {
+ FF_DEBUG("Firmware table size mismatch: NtQuerySystemInformation returned %lu but expected %lu",
+ bufSize, sfti.TableBufferLength + (ULONG) sizeof(sfti));
return NULL;
}
- FF_DEBUG("Firmware table size: %u bytes", bufSize);
+ FF_DEBUG("Firmware table size: %lu bytes", bufSize);
- buffer = (FFRawSmbiosData*) malloc(bufSize);
- assert(buffer);
+ buffer = malloc(bufSize);
+ *buffer = sfti;
FF_DEBUG("Allocated buffer for SMBIOS data");
- FF_MAYBE_UNUSED uint32_t resultSize = GetSystemFirmwareTable(signature, 0, buffer, bufSize);
- assert(resultSize == bufSize);
+ if (!NT_SUCCESS(NtQuerySystemInformation(SystemFirmwareTableInformation, buffer, bufSize, &bufSize)))
+ {
+ FF_DEBUG("NtQuerySystemInformation(SystemFirmwareTableInformation) failed");
+ free(buffer);
+ buffer = NULL;
+ return NULL;
+ }
+ FFRawSmbiosData* rawData = (FFRawSmbiosData*) buffer->TableBuffer;
+
FF_DEBUG("Successfully retrieved SMBIOS data: version %u.%u, length %u bytes",
- buffer->SMBIOSMajorVersion, buffer->SMBIOSMinorVersion, buffer->Length);
+ rawData->SMBIOSMajorVersion, rawData->SMBIOSMinorVersion, rawData->Length);
FF_DEBUG("Parsing SMBIOS table structures");
FF_MAYBE_UNUSED int structureCount = 0;
for (
- const FFSmbiosHeader* header = (const FFSmbiosHeader*) buffer->SMBIOSTableData;
- (const uint8_t*) header < buffer->SMBIOSTableData + buffer->Length;
+ const FFSmbiosHeader* header = (const FFSmbiosHeader*) rawData->SMBIOSTableData;
+ (const uint8_t*) header < rawData->SMBIOSTableData + rawData->Length;
header = ffSmbiosNextEntry(header)
)
{
diff --git a/src/common/impl/temps.c b/src/common/impl/temps.c
index c3c6f47f30..29d705aa41 100644
--- a/src/common/impl/temps.c
+++ b/src/common/impl/temps.c
@@ -109,6 +109,8 @@ bool ffTempsParseCommandOptions(const char* key, const char* subkey, const char*
bool ffTempsParseJsonObject(yyjson_val* key, yyjson_val* value, bool* useTemp, FFColorRangeConfig* config)
{
+ assert(key);
+
if (!unsafe_yyjson_equals_str(key, "temp"))
return false;
diff --git a/src/common/time.h b/src/common/time.h
index d8506d3b87..b4e0587d74 100644
--- a/src/common/time.h
+++ b/src/common/time.h
@@ -4,17 +4,9 @@
#include
#include
#ifdef _WIN32
- #include
#include
+ #include "common/windows/nt.h"
#include
- #include
-
- NTSYSCALLAPI
- NTSTATUS
- NTAPI
- NtDelayExecution(
- _In_ BOOLEAN Alertable,
- _In_ PLARGE_INTEGER DelayInterval);
#elif defined(__HAIKU__)
#include
#endif
@@ -40,8 +32,7 @@ static inline double ffTimeGetTick(void) //In msec
static inline uint64_t ffTimeGetNow(void)
{
#ifdef _WIN32
- uint64_t timeNow;
- GetSystemTimeAsFileTime((FILETIME*) &timeNow);
+ uint64_t timeNow = ffKSystemTimeToUInt64(&SharedUserData->SystemTime);
return (timeNow - 116444736000000000ull) / 10000ull;
#elif defined(__HAIKU__)
return (uint64_t) real_time_clock_usecs() / 1000u;
@@ -58,7 +49,7 @@ static inline bool ffTimeSleep(uint32_t msec)
#ifdef _WIN32
LARGE_INTEGER interval;
interval.QuadPart = -(int64_t) msec * 10000; // Relative time in 100-nanosecond intervals
- return NtDelayExecution(TRUE, &interval) == STATUS_SUCCESS;
+ return NT_SUCCESS(NtDelayExecution(TRUE, &interval));
#else
return nanosleep(&(struct timespec){ msec / 1000, (long) (msec % 1000) * 1000000 }, NULL) == 0;
#endif
diff --git a/src/common/windows/manifest.xml b/src/common/windows/manifest.xml
index 86d6231443..ed9a326347 100644
--- a/src/common/windows/manifest.xml
+++ b/src/common/windows/manifest.xml
@@ -16,7 +16,8 @@
UTF-8
- false
+ true/pm
+ PerMonitor
SegmentHeap
diff --git a/src/common/windows/nt.h b/src/common/windows/nt.h
index b4d83d66aa..446bd271fc 100644
--- a/src/common/windows/nt.h
+++ b/src/common/windows/nt.h
@@ -1,7 +1,18 @@
#pragma once
-#include
+#include
#include
+#include
+#include
+#include
+
+enum {
+ SystemModuleInformation = 11,
+ SystemFirmwareTableInformation = 76,
+ SystemBootEnvironmentInformation = 90,
+ SystemLogicalProcessorAndGroupInformation = 107,
+ SystemSecureBootInformation = 146,
+};
#define D3DKMT_ALIGN64 __attribute__((aligned(8)))
@@ -270,3 +281,392 @@ typedef struct _PROCESS_DEVICEMAP_INFORMATION_EX
#ifndef NtCurrentProcess
#define NtCurrentProcess() ((HANDLE)(LONG_PTR)-1)
#endif
+
+typedef struct _CURDIR
+{
+ UNICODE_STRING DosPath;
+ HANDLE Handle;
+} CURDIR, *PCURDIR;
+
+PIMAGE_NT_HEADERS NTAPI RtlImageNtHeader(IN PVOID BaseOfImage);
+
+/**
+ * The SECTION_IMAGE_INFORMATION structure contains detailed information about an image section.
+ */
+typedef struct _SECTION_IMAGE_INFORMATION
+{
+ PVOID TransferAddress; // The address of the image entry point function.
+ ULONG ZeroBits; // The number of high-order address bits that must be zero in the image base address.
+ SIZE_T MaximumStackSize; // The maximum stack size of threads from the PE file header.
+ SIZE_T CommittedStackSize; // The initial stack size of threads from the PE file header.
+ ULONG SubSystemType; // The image subsystem from the PE file header (e.g., Windows GUI, Windows CUI, POSIX).
+ union
+ {
+ struct
+ {
+ USHORT SubSystemMinorVersion;
+ USHORT SubSystemMajorVersion;
+ };
+ ULONG SubSystemVersion;
+ };
+ union
+ {
+ struct
+ {
+ USHORT MajorOperatingSystemVersion;
+ USHORT MinorOperatingSystemVersion;
+ };
+ ULONG OperatingSystemVersion;
+ };
+ USHORT ImageCharacteristics; // The image characteristics from the PE file header.
+ USHORT DllCharacteristics; // The DLL characteristics flags (e.g., ASLR, NX compatibility).
+ USHORT Machine; // The image architecture (e.g., x86, x64, ARM).
+ BOOLEAN ImageContainsCode; // The image contains native executable code.
+ union
+ {
+ UCHAR ImageFlags;
+ struct
+ {
+ UCHAR ComPlusNativeReady : 1; // The image contains precompiled .NET assembly generated by NGEN (Native Image Generator).
+ UCHAR ComPlusILOnly : 1; // the image contains only Microsoft Intermediate Language (IL) assembly.
+ UCHAR ImageDynamicallyRelocated : 1; // The image was mapped using a random base address rather than the preferred base address.
+ UCHAR ImageMappedFlat : 1; // The image was mapped using a single contiguous region, rather than separate regions for each section.
+ UCHAR BaseBelow4gb : 1; // The image was mapped using a base address below the 4 GB boundary.
+ UCHAR ComPlusPrefer32bit : 1; // The image prefers to run as a 32-bit process, even on a 64-bit system.
+ UCHAR Reserved : 2;
+ };
+ };
+ ULONG LoaderFlags; // Reserved by ntdll.dll for the Windows loader.
+ ULONG ImageFileSize; // The size of the image, in bytes, including all headers.
+ ULONG CheckSum; // The image file checksum, from the PE optional header.
+} SECTION_IMAGE_INFORMATION, *PSECTION_IMAGE_INFORMATION;
+
+typedef struct _SYSTEM_BOOT_ENVIRONMENT_INFORMATION
+{
+ GUID BootIdentifier;
+ FIRMWARE_TYPE FirmwareType;
+ union
+ {
+ ULONGLONG BootFlags;
+ struct
+ {
+ ULONGLONG DbgMenuOsSelection : 1; // REDSTONE4
+ ULONGLONG DbgHiberBoot : 1;
+ ULONGLONG DbgSoftBoot : 1;
+ ULONGLONG DbgMeasuredLaunch : 1;
+ ULONGLONG DbgMeasuredLaunchCapable : 1; // 19H1
+ ULONGLONG DbgSystemHiveReplace : 1;
+ ULONGLONG DbgMeasuredLaunchSmmProtections : 1;
+ ULONGLONG DbgMeasuredLaunchSmmLevel : 7; // 20H1
+ ULONGLONG DbgBugCheckRecovery : 1; // 24H2
+ ULONGLONG DbgFASR : 1;
+ ULONGLONG DbgUseCachedBcd : 1;
+ };
+ };
+} SYSTEM_BOOT_ENVIRONMENT_INFORMATION;
+
+typedef struct _RTL_PROCESS_MODULE_INFORMATION
+{
+ PVOID Section;
+ PVOID MappedBase;
+ PVOID ImageBase;
+ ULONG ImageSize;
+ ULONG Flags;
+ USHORT LoadOrderIndex;
+ USHORT InitOrderIndex;
+ USHORT LoadCount;
+ USHORT OffsetToFileName;
+ UCHAR FullPathName[256];
+} RTL_PROCESS_MODULE_INFORMATION, *PRTL_PROCESS_MODULE_INFORMATION;
+
+typedef struct _RTL_PROCESS_MODULES
+{
+ ULONG NumberOfModules;
+ _Field_size_(NumberOfModules) RTL_PROCESS_MODULE_INFORMATION Modules[1];
+} RTL_PROCESS_MODULES, *PRTL_PROCESS_MODULES;
+
+NTSTATUS NTAPI NtQuerySystemEnvironmentValueEx(
+ _In_ PCUNICODE_STRING VariableName,
+ _In_ const GUID* VendorGuid,
+ _Out_writes_bytes_opt_(*BufferLength) PVOID Buffer,
+ _Inout_ PULONG BufferLength,
+ _Out_opt_ PULONG Attributes // EFI_VARIABLE_*
+);
+
+NTSTATUS NTAPI RtlGUIDFromString(IN PCUNICODE_STRING GuidString, OUT GUID* Guid);
+
+typedef struct _SYSTEM_SECUREBOOT_INFORMATION
+{
+ BOOLEAN SecureBootEnabled;
+ BOOLEAN SecureBootCapable;
+} SYSTEM_SECUREBOOT_INFORMATION, *PSYSTEM_SECUREBOOT_INFORMATION;
+
+NTSTATUS NTAPI NtQuerySystemInformationEx(
+ _In_ SYSTEM_INFORMATION_CLASS SystemInformationClass,
+ _In_reads_bytes_(InputBufferLength) PVOID InputBuffer,
+ _In_ ULONG InputBufferLength,
+ _Out_writes_bytes_opt_(SystemInformationLength) PVOID SystemInformation,
+ _In_ ULONG SystemInformationLength,
+ _Out_opt_ PULONG ReturnLength
+);
+
+typedef enum _SYSTEM_FIRMWARE_TABLE_ACTION
+{
+ SystemFirmwareTableEnumerate,
+ SystemFirmwareTableGet,
+ SystemFirmwareTableMax
+} SYSTEM_FIRMWARE_TABLE_ACTION;
+
+typedef struct _SYSTEM_FIRMWARE_TABLE_INFORMATION
+{
+ ULONG ProviderSignature; // (same as the GetSystemFirmwareTable function)
+ SYSTEM_FIRMWARE_TABLE_ACTION Action;
+ ULONG TableID;
+ ULONG TableBufferLength;
+ _Field_size_bytes_(TableBufferLength) UCHAR TableBuffer[];
+} SYSTEM_FIRMWARE_TABLE_INFORMATION, *PSYSTEM_FIRMWARE_TABLE_INFORMATION;
+
+NTSTATUS NTAPI NtDelayExecution(_In_ BOOLEAN Alertable, _In_ PLARGE_INTEGER DelayInterval);
+
+/**
+ * The KSYSTEM_TIME structure represents interrupt time, system time, and time zone bias.
+ */
+typedef struct _KSYSTEM_TIME
+{
+ ULONG LowPart;
+ LONG High1Time;
+ LONG High2Time;
+} KSYSTEM_TIME, *PKSYSTEM_TIME;
+
+/**
+ * PROCESSOR_FEATURE_MAX defines the maximum number of processor feature flags
+ * that may be reported by the system.
+ */
+#define PROCESSOR_FEATURE_MAX 64
+
+/**
+ * The KUSER_SHARED_DATA structure contains information shared with user-mode.
+ *
+ * \sa https://learn.microsoft.com/en-us/windows-hardware/drivers/ddi/ntddk/ns-ntddk-kuser_shared_data
+ */
+typedef struct _KUSER_SHARED_DATA
+{
+ //
+ // Current low 32-bit of tick count and tick count multiplier.
+ //
+ // N.B. The tick count is updated each time the clock ticks.
+ //
+
+ ULONG TickCountLowDeprecated;
+ ULONG TickCountMultiplier;
+
+ //
+ // Current 64-bit interrupt time in 100ns units.
+ //
+
+ volatile KSYSTEM_TIME InterruptTime;
+
+ //
+ // Current 64-bit system time in 100ns units.
+ //
+
+ volatile KSYSTEM_TIME SystemTime;
+
+ //
+ // Current 64-bit time zone bias.
+ //
+
+ volatile KSYSTEM_TIME TimeZoneBias;
+
+ //
+ // Support image magic number range for the host system.
+ //
+ // N.B. This is an inclusive range.
+ //
+
+ USHORT ImageNumberLow;
+ USHORT ImageNumberHigh;
+
+ //
+ // Copy of system root in unicode.
+ //
+ // N.B. This field must be accessed via the RtlGetNtSystemRoot API for
+ // an accurate result.
+ //
+
+ WCHAR NtSystemRoot[260];
+
+ //
+ // Maximum stack trace depth if tracing enabled.
+ //
+
+ ULONG MaxStackTraceDepth;
+
+ //
+ // Crypto exponent value.
+ //
+
+ ULONG CryptoExponent;
+
+ //
+ // Time zone ID.
+ //
+
+ ULONG TimeZoneId;
+
+ //
+ // Minimum size of a large page on the system, in bytes.
+ //
+ // N.B. Returned by GetLargePageMinimum() function.
+ //
+
+ ULONG LargePageMinimum;
+
+ //
+ // This value controls the Application Impact Telemetry (AIT) Sampling rate.
+ //
+ // This value determines how frequently the system records AIT events,
+ // which are used by the Application Experience and compatibility
+ // subsystems to evaluate application behavior, performance, and
+ // potential compatibility issues.
+ //
+ // Lower values increase sampling frequency, while higher values reduce it.
+ // The kernel updates this field as part of its internal telemetry and
+ // heuristics logic.
+ //
+
+ ULONG AitSamplingValue;
+
+ //
+ // This value controls Application Compatibility (AppCompat) switchback processing.
+ //
+
+ union
+ {
+ ULONG AppCompatFlag;
+ struct
+ {
+ ULONG SwitchbackEnabled : 1; // Basic switchback processing
+ ULONG ExtendedHeuristics : 1; // Extended switchback heuristics
+ ULONG TelemetryFallback : 1; // Telemetry-driven fallback
+ ULONG Reserved : 29;
+ } AppCompatFlags;
+ };
+
+ //
+ // Current Kernel Root RNG state seed version
+ //
+
+ ULONGLONG RNGSeedVersion;
+
+ //
+ // This value controls assertion failure handling.
+ //
+ // Historically (prior to Windows 10), this value was also used by
+ // Code Integrity (CI), AppLocker, and related security components to
+ // determine the minimum validation requirements for executable images,
+ // drivers, and privileged operations.
+ //
+ // In modern Windows versions, this field is used primarily by the kernel's
+ // diagnostic and validation infrastructure to decide how assertion failures
+ // should be handled (e.g., logging, debugger break-in, or bugcheck).
+
+ ULONG GlobalValidationRunlevel;
+
+ //
+ // Monotonic stamp incremented by the kernel whenever the system's
+ // time zone bias value changes.
+ //
+ // N.B. This field must be accessed via the RtlGetSystemTimeAndBias API for
+ // an accurate result.
+ // This value is read before and after accessing the bias fields to determine
+ // whether the time zone data changed during the read. If the stamp differs,
+ // the caller must re-read the bias values to ensure consistency.
+ //
+
+ volatile LONG TimeZoneBiasStamp;
+
+ //
+ // The shared collective build number undecorated with C or F.
+ // GetVersionEx hides the real number
+ //
+
+ ULONG NtBuildNumber;
+
+ //
+ // Product type.
+ //
+ // N.B. This field must be accessed via the RtlGetNtProductType API for
+ // an accurate result.
+ //
+
+ NT_PRODUCT_TYPE NtProductType;
+ BOOLEAN ProductTypeIsValid;
+ BOOLEAN Reserved0[1];
+
+ //
+ // Native hardware processor architecture of the running system.
+ //
+ // N.B. User-mode components read this field to determine the true system
+ // architecture, especially in WOW64 scenarios where the process architecture
+ // differs from the native one.
+ //
+
+ USHORT NativeProcessorArchitecture;
+
+ //
+ // The NT Version.
+ //
+ // N. B. Note that each process sees a version from its PEB, but if the
+ // process is running with an altered view of the system version,
+ // the following two fields are used to correctly identify the
+ // version
+ //
+
+ ULONG NtMajorVersion;
+ ULONG NtMinorVersion;
+
+ //
+ // Processor features.
+ //
+
+ BOOLEAN ProcessorFeatures[PROCESSOR_FEATURE_MAX];
+
+ // ... more fields follow, but we don't need them
+} KUSER_SHARED_DATA, *PKUSER_SHARED_DATA;
+
+#define SharedUserData ((const KUSER_SHARED_DATA*) 0x7FFE0000UL)
+
+static inline uint64_t ffKSystemTimeToUInt64(const volatile KSYSTEM_TIME* pTime)
+{
+ #if _WIN64
+
+ return *(uint64_t*) pTime;
+
+ #else
+
+ uint32_t low, high1, high2;
+
+ do {
+ high1 = pTime->High1Time;
+ low = pTime->LowPart;
+ high2 = pTime->High2Time;
+ } while (high1 != high2);
+
+ return ((uint64_t) high1 << 32) | low;
+ #endif
+}
+
+static inline bool ffIsWindows10OrGreater()
+{
+ #if FF_WIN7_COMPAT
+ return SharedUserData->NtMajorVersion >= 10;
+ #else
+ return true;
+ #endif
+}
+
+static inline bool ffIsWindows11OrGreater()
+{
+ return ffIsWindows10OrGreater() && SharedUserData->NtBuildNumber >= 22000;
+}
diff --git a/src/common/windows/variant.cpp b/src/common/windows/variant.cpp
new file mode 100644
index 0000000000..fa3b9a0e54
--- /dev/null
+++ b/src/common/windows/variant.cpp
@@ -0,0 +1,21 @@
+#include "variant.hpp"
+
+#include
+
+FFWmiVariant::FFWmiVariant(std::initializer_list strings): FFWmiVariant()
+{
+ SAFEARRAYBOUND bound = {
+ .cElements = (ULONG) strings.size(),
+ .lLbound = 0,
+ };
+ SAFEARRAY* psa = SafeArrayCreate(VT_BSTR, 1, &bound);
+
+ LONG i = 0;
+ for (PCWSTR str : strings) {
+ SafeArrayPutElement(psa, &i, bstr_t(str));
+ ++i;
+ }
+
+ this->vt = VT_ARRAY | VT_BSTR;
+ this->parray = psa;
+}
diff --git a/src/common/windows/variant.hpp b/src/common/windows/variant.hpp
index 84a471ff9c..b2e68cfa8f 100644
--- a/src/common/windows/variant.hpp
+++ b/src/common/windows/variant.hpp
@@ -3,6 +3,8 @@
#include
#include
#include
+#include
+#include
template
struct FFBaseVariant: TVariant
@@ -137,6 +139,7 @@ struct FFWmiVariant: FFBaseVariant
FFWmiVariant(const FFWmiVariant&) = delete;
FFWmiVariant(FFWmiVariant&&); // don't define it to enforce NRVO optimization
explicit FFWmiVariant() { VariantInit(this); }
+ explicit FFWmiVariant(std::initializer_list strings);
~FFWmiVariant() { VariantClear(this); }
};
static_assert(sizeof(FFWmiVariant) == sizeof(VARIANT), "");
@@ -149,3 +152,18 @@ struct FFPropVariant: FFBaseVariant
~FFPropVariant() { PropVariantClear(this); }
};
static_assert(sizeof(FFPropVariant) == sizeof(PROPVARIANT), "");
+
+namespace
+{
+ // Provide our bstr_t to avoid libstdc++ dependency
+ struct bstr_t
+ {
+ explicit bstr_t(const wchar_t* str) noexcept: _bstr(SysAllocString(str)) {}
+ ~bstr_t(void) noexcept { SysFreeString(_bstr); }
+ explicit operator const wchar_t*(void) const noexcept { return _bstr; }
+ operator BSTR(void) const noexcept { return _bstr; }
+
+ private:
+ BSTR _bstr;
+ };
+}
diff --git a/src/common/windows/version.c b/src/common/windows/version.c
index 4019edb849..6d5ceefa18 100644
--- a/src/common/windows/version.c
+++ b/src/common/windows/version.c
@@ -4,6 +4,8 @@
#include
+#define FF_VERSION_LANG_EN_US L"040904b0"
+
bool ffGetFileVersion(const wchar_t* filePath, const wchar_t* stringName, FFstrbuf* version)
{
DWORD handle;
@@ -19,40 +21,25 @@ bool ffGetFileVersion(const wchar_t* filePath, const wchar_t* stringName, FFstrb
UINT len;
if (VerQueryValueW(versionData, L"\\", (void **)&verInfo, &len) && len && verInfo->dwSignature == 0xFEEF04BD)
{
- ffStrbufAppendF(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));
+ 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
{
- struct
- {
- WORD language;
- WORD codePage;
- }* translations;
-
- UINT translationsLen;
+ wchar_t* value;
+ UINT valueLen;
- if (VerQueryValueW(versionData, L"\\VarFileInfo\\Translation",
- (void **) &translations, &translationsLen) &&
- translationsLen >= sizeof(*translations))
+ 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)
{
- wchar_t subBlock[128];
- snwprintf(subBlock, ARRAY_SIZE(subBlock), L"\\StringFileInfo\\%04x%04x\\%ls",
- translations[0].language, translations[0].codePage, stringName);
-
- wchar_t* value;
- UINT valueLen;
-
- if (VerQueryValueW(versionData, subBlock, (void **)&value, &valueLen) && valueLen > 0)
- {
- ffStrbufSetWS(version, value);
- return true;
- }
+ ffStrbufSetWS(version, value);
+ return true;
}
}
}
diff --git a/src/common/windows/wmi.hpp b/src/common/windows/wmi.hpp
index c506a8868d..a72a72dccf 100644
--- a/src/common/windows/wmi.hpp
+++ b/src/common/windows/wmi.hpp
@@ -8,7 +8,6 @@ extern "C" {
#include
#include
-#include
#include "variant.hpp"
@@ -74,21 +73,6 @@ struct FFWmiQuery
}
};
-namespace
-{
- // Provide our bstr_t to avoid libstdc++ dependency
- struct bstr_t
- {
- explicit bstr_t(const wchar_t* str) noexcept: _bstr(SysAllocString(str)) {}
- ~bstr_t(void) noexcept { SysFreeString(_bstr); }
- explicit operator const wchar_t*(void) const noexcept { return _bstr; }
- operator BSTR(void) const noexcept { return _bstr; }
-
- private:
- BSTR _bstr;
- };
-}
-
#else
// Win32 COM headers requires C++ compiler
#error Must be included in C++ source file
diff --git a/src/detection/battery/battery_windows.c b/src/detection/battery/battery_windows.c
index ba0111d3a0..3196885115 100644
--- a/src/detection/battery/battery_windows.c
+++ b/src/detection/battery/battery_windows.c
@@ -11,7 +11,6 @@
#include
#include
#include
-#include
static const char* detectWithCmApi(FFBatteryOptions* options, FFlist* results)
{
diff --git a/src/detection/bios/bios_windows.c b/src/detection/bios/bios_windows.c
index 059ebce464..716add3704 100644
--- a/src/detection/bios/bios_windows.c
+++ b/src/detection/bios/bios_windows.c
@@ -5,28 +5,7 @@
#include "common/windows/registry.h"
#include
-#include
-
-typedef struct _SYSTEM_BOOT_ENVIRONMENT_INFORMATION
-{
- GUID BootIdentifier;
- FIRMWARE_TYPE FirmwareType;
- union
- {
- ULONGLONG BootFlags;
- struct
- {
- ULONGLONG DbgMenuOsSelection : 1; // REDSTONE4
- ULONGLONG DbgHiberBoot : 1;
- ULONGLONG DbgSoftBoot : 1;
- ULONGLONG DbgMeasuredLaunch : 1;
- ULONGLONG DbgMeasuredLaunchCapable : 1; // 19H1
- ULONGLONG DbgSystemHiveReplace : 1;
- ULONGLONG DbgMeasuredLaunchSmmProtections : 1;
- ULONGLONG DbgMeasuredLaunchSmmLevel : 7; // 20H1
- };
- };
-} SYSTEM_BOOT_ENVIRONMENT_INFORMATION;
+#include "common/windows/nt.h"
#elif __OpenBSD__
#include "common/io.h"
@@ -90,7 +69,7 @@ const char* ffDetectBios(FFBiosResult* bios)
// Same as GetFirmwareType, but support (?) Windows 7
// https://ntdoc.m417z.com/system_information_class
SYSTEM_BOOT_ENVIRONMENT_INFORMATION sbei;
- if (NT_SUCCESS(NtQuerySystemInformation(90 /*SystemBootEnvironmentInformation*/, &sbei, sizeof(sbei), NULL)))
+ if (NT_SUCCESS(NtQuerySystemInformation(SystemBootEnvironmentInformation, &sbei, sizeof(sbei), NULL)))
{
switch (sbei.FirmwareType)
{
diff --git a/src/detection/bluetooth/bluetooth_linux.c b/src/detection/bluetooth/bluetooth_linux.c
index 162e60a51c..4f8c1c8039 100644
--- a/src/detection/bluetooth/bluetooth_linux.c
+++ b/src/detection/bluetooth/bluetooth_linux.c
@@ -191,7 +191,7 @@ static void detectBluetoothRoot(FFlist* devices, FFDBusData* dbus, DBusMessageIt
static const char* detectBluetooth(FFlist* devices, int32_t connectedCount)
{
- FFDBusData dbus;
+ FF_DBUS_AUTO_DESTROY_DATA FFDBusData dbus = {};
const char* error = ffDBusLoadData(DBUS_BUS_SYSTEM, &dbus);
if(error)
return error;
diff --git a/src/detection/bluetooth/bluetooth_windows.cpp b/src/detection/bluetooth/bluetooth_windows.cpp
index b54882d613..3109037f4d 100644
--- a/src/detection/bluetooth/bluetooth_windows.cpp
+++ b/src/detection/bluetooth/bluetooth_windows.cpp
@@ -6,8 +6,6 @@ extern "C"
#include "common/windows/unicode.hpp"
#include "common/windows/util.hpp"
-STDAPI InitVariantFromStringArray(_In_reads_(cElems) PCWSTR *prgsz, _In_ ULONG cElems, _Out_ VARIANT *pvar);
-
extern "C"
const char* ffBluetoothDetectBattery(FFlist* devices)
{
@@ -27,13 +25,7 @@ const char* ffBluetoothDetectBattery(FFlist* devices)
if (FAILED(pnpEntityClass->GetMethod(bstr_t(L"GetDeviceProperties"), 0, &pInParams, NULL)))
return "Failed to get GetDeviceProperties method";
- VARIANT devicePropertyKeys;
- PCWSTR props[] = { L"{104EA319-6EE2-4701-BD47-8DDBF425BBE5} 2", L"DEVPKEY_Bluetooth_DeviceAddress" };
-
- if (FAILED(InitVariantFromStringArray(props, ARRAY_SIZE(props), &devicePropertyKeys)))
- return "Failed to init variant from string array";
- on_scope_exit releaseDevicePropertyKeys([&] { VariantClear(&devicePropertyKeys); });
-
+ FFWmiVariant devicePropertyKeys({ L"{104EA319-6EE2-4701-BD47-8DDBF425BBE5} 2", L"DEVPKEY_Bluetooth_DeviceAddress" });
if (FAILED(pInParams->Put(L"devicePropertyKeys", 0, &devicePropertyKeys, CIM_FLAG_ARRAY | CIM_STRING)))
return "Failed to put devicePropertyKeys";
}
diff --git a/src/detection/bluetoothradio/bluetoothradio_linux.c b/src/detection/bluetoothradio/bluetoothradio_linux.c
index f7f3971ba8..1583d43d08 100644
--- a/src/detection/bluetoothradio/bluetoothradio_linux.c
+++ b/src/detection/bluetoothradio/bluetoothradio_linux.c
@@ -112,7 +112,7 @@ static const char* detectBluetooth(FFlist* devices)
if(dirp == NULL)
return "Failed to open /sys/class/bluetooth";
- FFDBusData dbus;
+ FF_DBUS_AUTO_DESTROY_DATA FFDBusData dbus = {};
const char* error = ffDBusLoadData(DBUS_BUS_SYSTEM, &dbus);
if(error)
return error;
diff --git a/src/detection/bootmgr/bootmgr_windows.c b/src/detection/bootmgr/bootmgr_windows.c
index 3fe46b161f..e8aced1372 100644
--- a/src/detection/bootmgr/bootmgr_windows.c
+++ b/src/detection/bootmgr/bootmgr_windows.c
@@ -34,21 +34,30 @@ const char* ffDetectBootmgr(FFBootmgrResult* result)
if (enablePrivilege(L"SeSystemEnvironmentPrivilege") != NULL)
return "Failed to enable SeSystemEnvironmentPrivilege";
- if (GetFirmwareEnvironmentVariableW(L"BootCurrent", L"{" FF_EFI_GLOBAL_GUID L"}", &result->order, sizeof(result->order)) != 2)
- return "GetFirmwareEnvironmentVariableW(BootCurrent) failed";
+ GUID efiGlobalGuid;
+ if (!NT_SUCCESS(RtlGUIDFromString(&(UNICODE_STRING) RTL_CONSTANT_STRING(L"{" FF_EFI_GLOBAL_GUID L"}"), &efiGlobalGuid)))
+ return "RtlGUIDFromString() failed";
+
+ ULONG size = sizeof(result->order);
+ if (!NT_SUCCESS(NtQuerySystemEnvironmentValueEx(&(UNICODE_STRING) RTL_CONSTANT_STRING(L"BootCurrent"), &efiGlobalGuid, &result->order, &size, NULL)))
+ return "NtQuerySystemEnvironmentValueEx(BootCurrent) failed";
+ if (size != sizeof(result->order))
+ return "NtQuerySystemEnvironmentValueEx(BootCurrent) returned unexpected size";
uint8_t buffer[2048];
- wchar_t key[16];
+ wchar_t key[9];
swprintf(key, ARRAY_SIZE(key), L"Boot%04X", result->order);
- uint32_t size = GetFirmwareEnvironmentVariableW(key, L"{" FF_EFI_GLOBAL_GUID L"}", buffer, sizeof(buffer));
+ size = sizeof(buffer);
+ if (!NT_SUCCESS(NtQuerySystemEnvironmentValueEx(&(UNICODE_STRING) RTL_CONSTANT_STRING(key), &efiGlobalGuid, buffer, &size, NULL)))
+ return "NtQuerySystemEnvironmentValueEx(Boot####) failed";
if (size < sizeof(FFEfiLoadOption) || size == ARRAY_SIZE(buffer))
- return "GetFirmwareEnvironmentVariableW(Boot####) failed";
+ return "NtQuerySystemEnvironmentValueEx(Boot####) returned unexpected size";
ffEfiFillLoadOption((FFEfiLoadOption *)buffer, result);
- DWORD uefiSecureBootEnabled = 0, bufSize = 0;
- if (RegGetValueW(HKEY_LOCAL_MACHINE, L"SYSTEM\\CurrentControlSet\\Control\\SecureBoot\\State", L"UEFISecureBootEnabled", RRF_RT_REG_DWORD, NULL, &uefiSecureBootEnabled, &bufSize) == ERROR_SUCCESS)
- result->secureBoot = !!uefiSecureBootEnabled;
+ SYSTEM_SECUREBOOT_INFORMATION ssi;
+ if (NT_SUCCESS(NtQuerySystemInformation(SystemSecureBootInformation, &ssi, sizeof(ssi), NULL)))
+ result->secureBoot = ssi.SecureBootEnabled;
return NULL;
}
diff --git a/src/detection/cpu/cpu_windows.c b/src/detection/cpu/cpu_windows.c
index 1e1333d9c0..c0555ec21d 100644
--- a/src/detection/cpu/cpu_windows.c
+++ b/src/detection/cpu/cpu_windows.c
@@ -6,6 +6,7 @@
#include
#include "common/windows/perflib_.h"
+#include "common/windows/nt.h"
#include
static inline void ffPerfCloseQueryHandle(HANDLE* phQuery)
@@ -212,15 +213,16 @@ static const char* detectMaxSpeedBySmbios(FFCPUResult* cpu)
static const char* detectNCores(FFCPUResult* cpu)
{
- DWORD length = 0;
- GetLogicalProcessorInformationEx(RelationAll, NULL, &length);
+ LOGICAL_PROCESSOR_RELATIONSHIP lpr = RelationAll;
+ ULONG length = 0;
+ NtQuerySystemInformationEx(SystemLogicalProcessorAndGroupInformation, &lpr, sizeof(lpr), NULL, 0, &length);
if (length == 0)
return "GetLogicalProcessorInformationEx(RelationAll, NULL, &length) failed";
SYSTEM_LOGICAL_PROCESSOR_INFORMATION_EX* FF_AUTO_FREE
pProcessorInfo = (SYSTEM_LOGICAL_PROCESSOR_INFORMATION_EX*)malloc(length);
- if (!pProcessorInfo || !GetLogicalProcessorInformationEx(RelationAll, pProcessorInfo, &length))
+ if (!NT_SUCCESS(NtQuerySystemInformationEx(SystemLogicalProcessorAndGroupInformation, &lpr, sizeof(lpr), pProcessorInfo, length, &length)))
return "GetLogicalProcessorInformationEx(RelationAll, pProcessorInfo, &length) failed";
for(
diff --git a/src/detection/cpucache/cpucache_windows.c b/src/detection/cpucache/cpucache_windows.c
index e04f276e01..537a69a75f 100644
--- a/src/detection/cpucache/cpucache_windows.c
+++ b/src/detection/cpucache/cpucache_windows.c
@@ -1,19 +1,19 @@
#include "cpucache.h"
#include "common/mallocHelper.h"
-
-#include
+#include "common/windows/nt.h"
const char* ffDetectCPUCache(FFCPUCacheResult* result)
{
+ LOGICAL_PROCESSOR_RELATIONSHIP lpr = RelationCache;
DWORD length = 0;
- GetLogicalProcessorInformationEx(RelationCache, NULL, &length);
+ NtQuerySystemInformationEx(SystemLogicalProcessorAndGroupInformation, &lpr, sizeof(lpr), NULL, 0, &length);
if (length == 0)
return "GetLogicalProcessorInformationEx(RelationCache, NULL, &length) failed";
SYSTEM_LOGICAL_PROCESSOR_INFORMATION_EX* FF_AUTO_FREE
pProcessorInfo = (SYSTEM_LOGICAL_PROCESSOR_INFORMATION_EX*)malloc(length);
- if (!pProcessorInfo || !GetLogicalProcessorInformationEx(RelationCache, pProcessorInfo, &length))
+ if (!NT_SUCCESS(NtQuerySystemInformationEx(SystemLogicalProcessorAndGroupInformation, &lpr, sizeof(lpr), pProcessorInfo, length, &length)))
return "GetLogicalProcessorInformationEx(RelationCache, pProcessorInfo, &length) failed";
for(
diff --git a/src/detection/cpuusage/cpuusage_windows.c b/src/detection/cpuusage/cpuusage_windows.c
index 89ce1a1a09..1e96f43d54 100644
--- a/src/detection/cpuusage/cpuusage_windows.c
+++ b/src/detection/cpuusage/cpuusage_windows.c
@@ -1,13 +1,12 @@
-#include "fastfetch.h"
#include "detection/cpuusage/cpuusage.h"
-
#include "common/mallocHelper.h"
+#include "common/debug.h"
#include
-#include
#include
#include
#include "common/windows/perflib_.h"
+#include "common/windows/nt.h"
static const char* getInfoByNqsi(FFlist* cpuTimes)
{
@@ -155,20 +154,17 @@ static const char* getInfoByPerflib(FFlist* cpuTimes)
const char* ffGetCpuUsageInfo(FFlist* cpuTimes)
{
- #if !FF_WIN7_COMPAT
- static uint8_t winver = 10; // Assume Windows 10 or later for WoA
- #else
- static uint8_t winver = 0;
- if (winver == 0)
- winver = (uint8_t) ffStrbufToUInt(&instance.state.platform.sysinfo.release, 1);
- #endif
-
- if (winver >= 10)
+ const char* error = NULL;
+
+ if (ffIsWindows10OrGreater())
{
- if (getInfoByPerflib(cpuTimes) == NULL) return NULL;
+ error = getInfoByPerflib(cpuTimes);
+ FF_DEBUG("Get CPU usage info by Perflib: %s", error ?: "success");
+ if (!error) return NULL;
ffListClear(cpuTimes);
- winver = 1; // Fall back to NQSI
}
- return getInfoByNqsi(cpuTimes);
+ error = getInfoByNqsi(cpuTimes);
+ FF_DEBUG("Get CPU usage info by NtQuerySystemInformation: %s", error ?: "success");
+ return error;
}
diff --git a/src/detection/de/de_linux.c b/src/detection/de/de_linux.c
index ee2aeafcc5..b7ac7a6edc 100644
--- a/src/detection/de/de_linux.c
+++ b/src/detection/de/de_linux.c
@@ -58,7 +58,7 @@ static void getKDE(FFstrbuf* result, FF_MAYBE_UNUSED FFDEOptions* options)
static const char* getGnomeByDbus(FF_MAYBE_UNUSED FFstrbuf* result)
{
#ifdef FF_HAVE_DBUS
- FFDBusData dbus;
+ FF_DBUS_AUTO_DESTROY_DATA FFDBusData dbus = {};
if (ffDBusLoadData(DBUS_BUS_SESSION, &dbus) != NULL)
return "ffDBusLoadData() failed";
diff --git a/src/detection/disk/disk_windows.c b/src/detection/disk/disk_windows.c
index 47cd3f504e..50545338e5 100644
--- a/src/detection/disk/disk_windows.c
+++ b/src/detection/disk/disk_windows.c
@@ -18,9 +18,7 @@ const char* ffDetectDisksImpl(FFDiskOptions* options, FFlist* disks)
// For cross-platform portability; used by `presets/examples/13.jsonc`
if (options->folders.length == 1 && options->folders.chars[0] == '/')
{
- wchar_t path[MAX_PATH + 1];
- GetSystemWindowsDirectoryW(path, ARRAY_SIZE(path));
- options->folders.chars[0] = (char) path[0];
+ options->folders.chars[0] = (char) SharedUserData->NtSystemRoot[0];
ffStrbufAppendS(&options->folders, ":\\");
}
diff --git a/src/detection/displayserver/displayserver_windows.c b/src/detection/displayserver/displayserver_windows.c
index b18e502dbd..ab2cfbd1c8 100644
--- a/src/detection/displayserver/displayserver_windows.c
+++ b/src/detection/displayserver/displayserver_windows.c
@@ -2,41 +2,30 @@
#include "common/windows/unicode.h"
#include "common/edidHelper.h"
-#include
-#include
-#include
+#include
+#include
-typedef struct FFMonitorInfo
-{
- HMONITOR handle;
- MONITORINFOEXW info;
-} FFMonitorInfo;
-
-static CALLBACK BOOL MonitorEnumProc(
- HMONITOR hMonitor,
- FF_MAYBE_UNUSED HDC hdc,
- FF_MAYBE_UNUSED LPRECT lpRect,
- LPARAM lParam
-)
-{
- FFlist* monitors = (FFlist*) lParam;
- FFMonitorInfo* newMonitor = ffListAdd(monitors);
- newMonitor->handle = hMonitor;
- newMonitor->info.cbSize = sizeof(newMonitor->info);
-
- return GetMonitorInfoW(hMonitor, (MONITORINFO*) &newMonitor->info);
-}
+// 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)
{
- FF_LIST_AUTO_DESTROY monitors = ffListCreate(sizeof(FFMonitorInfo));
- EnumDisplayMonitors(NULL, NULL, MonitorEnumProc, (LPARAM) &monitors);
-
#if FF_WIN7_COMPAT
- HDC hdc = GetDC(NULL);
- uint32_t systemDpi = (uint32_t) GetDeviceCaps(hdc, LOGPIXELSX);
- if (systemDpi == 0) systemDpi = 96;
- ReleaseDC(NULL, hdc);
+ 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];
@@ -54,7 +43,8 @@ static void detectDisplays(FFDisplayServerResult* ds)
{
for (uint32_t i = 0; i < pathCount; ++i)
{
- DISPLAYCONFIG_PATH_INFO* path = &paths[i];
+ const DISPLAYCONFIG_PATH_INFO* path = &paths[i];
+ const DISPLAYCONFIG_SOURCE_MODE* sourceMode = &modes[path->sourceInfo.modeInfoIdx].sourceMode;
DISPLAYCONFIG_SOURCE_DEVICE_NAME sourceName = {
.header = {
@@ -65,20 +55,6 @@ static void detectDisplays(FFDisplayServerResult* ds)
},
};
- FFMonitorInfo* monitorInfo = NULL;
- if (DisplayConfigGetDeviceInfo(&sourceName.header) == ERROR_SUCCESS)
- {
- FF_LIST_FOR_EACH(FFMonitorInfo, item, monitors)
- {
- if (wcsncmp(item->info.szDevice, sourceName.viewGdiDeviceName, ARRAY_SIZE(sourceName.viewGdiDeviceName)) == 0)
- {
- monitorInfo = item;
- break;
- }
- }
- }
- if (!monitorInfo) continue;
-
FF_STRBUF_AUTO_DESTROY name = ffStrbufCreate();
uint32_t physicalWidth = 0, physicalHeight = 0;
@@ -131,8 +107,8 @@ static void detectDisplays(FFDisplayServerResult* ds)
}
}
- uint32_t width = modes[path->sourceInfo.modeInfoIdx].sourceMode.width;
- uint32_t height = modes[path->sourceInfo.modeInfoIdx].sourceMode.height;
+ uint32_t width = sourceMode->width;
+ uint32_t height = sourceMode->height;
if (path->targetInfo.rotation == DISPLAYCONFIG_ROTATION_ROTATE90 ||
path->targetInfo.rotation == DISPLAYCONFIG_ROTATION_ROTATE270)
{
@@ -168,21 +144,32 @@ static void detectDisplays(FFDisplayServerResult* ds)
preferredRefreshRate = freq.Numerator / (double) freq.Denominator;
}
- uint32_t scaledWidth = (uint32_t) (monitorInfo->info.rcMonitor.right - monitorInfo->info.rcMonitor.left);
- uint32_t scaledHeight = (uint32_t) (monitorInfo->info.rcMonitor.bottom - monitorInfo->info.rcMonitor.top);
+ uint32_t systemDpi = 0;
+
+ if (ffGetDpiForMonitor)
+ {
+ HMONITOR hMonitor = MonitorFromPoint(*(POINT*)&sourceMode->position, MONITOR_DEFAULTTONULL);
+ if (hMonitor)
+ {
+ UINT ignored;
+ ffGetDpiForMonitor(hMonitor, MDT_EFFECTIVE_DPI, &systemDpi, &ignored);
+ }
+ }
+
+ if (systemDpi == 0)
+ {
+ HDC hdc = GetDC(NULL);
+ systemDpi = (uint32_t) GetDeviceCaps(hdc, LOGPIXELSX);
+ if (systemDpi == 0) systemDpi = 96;
+ ReleaseDC(NULL, hdc);
+ }
FFDisplayResult* display = ffdsAppendDisplay(ds,
width,
height,
path->targetInfo.refreshRate.Numerator / (double) path->targetInfo.refreshRate.Denominator,
- #if FF_WIN7_COMPAT
- // Windows 7 always reports scaled width as the real width, as I tested on VM with 200% scaling.
- scaledWidth == width ? width * 96 / systemDpi : scaledWidth,
- scaledHeight == height ? height * 96 / systemDpi : scaledHeight,
- #else
- scaledWidth,
- scaledHeight,
- #endif
+ width * 96 / systemDpi,
+ height * 96 / systemDpi,
preferredMode.width,
preferredMode.height,
preferredRefreshRate,
@@ -193,8 +180,8 @@ static void detectDisplays(FFDisplayServerResult* ds)
path->targetInfo.outputTechnology == DISPLAYCONFIG_OUTPUT_TECHNOLOGY_DISPLAYPORT_EMBEDDED ||
path->targetInfo.outputTechnology == DISPLAYCONFIG_OUTPUT_TECHNOLOGY_UDI_EMBEDDED
? FF_DISPLAY_TYPE_BUILTIN : FF_DISPLAY_TYPE_EXTERNAL,
- !!(monitorInfo->info.dwFlags & MONITORINFOF_PRIMARY),
- (uint64_t)(uintptr_t) monitorInfo->handle,
+ sourceMode->position.x == 0 && sourceMode->position.y == 0,
+ path->sourceInfo.id,
physicalWidth,
physicalHeight,
"GDI"
@@ -253,8 +240,7 @@ static void detectDisplays(FFDisplayServerResult* ds)
void ffConnectDisplayServerImpl(FFDisplayServerResult* ds)
{
- BOOL enabled;
- if(SUCCEEDED(DwmIsCompositionEnabled(&enabled)) && enabled)
+ if (IsThreadDesktopComposited())
{
ffStrbufSetStatic(&ds->wmProcessName, "dwm.exe");
ffStrbufSetStatic(&ds->wmPrettyName, "Desktop Window Manager");
diff --git a/src/detection/displayserver/linux/wmde.c b/src/detection/displayserver/linux/wmde.c
index 5037215894..8454e51f66 100644
--- a/src/detection/displayserver/linux/wmde.c
+++ b/src/detection/displayserver/linux/wmde.c
@@ -226,9 +226,12 @@ static void applyPrettyNameIfDE(FFDisplayServerResult* result, const char* name)
) {
ffStrbufSetS(&result->deProcessName, "lxqt-session");
ffStrbufSetS(&result->dePrettyName, FF_DE_PRETTY_LXQT);
- FF_STRBUF_AUTO_DESTROY wmProcessNameBuffer = ffStrbufCreate();
- ffParsePropFileConfig("lxqt/session.conf", "window_manager =", &wmProcessNameBuffer);
- applyBetterWM(result, wmProcessNameBuffer.chars);
+ if (result->wmProcessName.length == 0)
+ {
+ FF_STRBUF_AUTO_DESTROY wmProcessNameBuffer = ffStrbufCreate();
+ ffParsePropFileConfig("lxqt/session.conf", "window_manager =", &wmProcessNameBuffer);
+ applyBetterWM(result, wmProcessNameBuffer.chars);
+ }
}
else if(
diff --git a/src/detection/displayserver/linux/xcb.c b/src/detection/displayserver/linux/xcb.c
index 7d24448b54..ce19f02c17 100644
--- a/src/detection/displayserver/linux/xcb.c
+++ b/src/detection/displayserver/linux/xcb.c
@@ -78,6 +78,19 @@ static void* xcbGetProperty(XcbRandrData* data, xcb_window_t window, const char*
return replyValue;
}
+static xcb_randr_get_output_property_reply_t* xcbRandrGetProperty(XcbRandrData* data, xcb_randr_output_t output, const char* name)
+{
+ xcb_intern_atom_cookie_t requestAtomCookie = data->ffxcb_intern_atom(data->connection, true, (uint16_t) strlen(name), name);
+ FF_AUTO_FREE xcb_intern_atom_reply_t* requestAtomReply = data->ffxcb_intern_atom_reply(data->connection, requestAtomCookie, NULL);
+
+ if(requestAtomReply)
+ {
+ xcb_randr_get_output_property_cookie_t outputPropertyCookie = data->ffxcb_randr_get_output_property(data->connection, output, requestAtomReply->atom, XCB_GET_PROPERTY_TYPE_ANY, 0, 100, false, false);
+ return data->ffxcb_randr_get_output_property_reply(data->connection, outputPropertyCookie, NULL);
+ }
+ return NULL;
+}
+
static void xcbDetectWMfromEWMH(XcbRandrData* data, xcb_window_t rootWindow, FFDisplayServerResult* result)
{
if(result->wmProcessName.length > 0 || ffStrbufCompS(&result->wmProtocolName, FF_WM_PROTOCOL_WAYLAND) == 0)
@@ -121,28 +134,34 @@ static bool xcbRandrHandleOutput(XcbRandrData* data, xcb_randr_output_t output,
if(outputInfoReply == NULL)
return false;
- xcb_intern_atom_cookie_t requestAtomCookie = data->ffxcb_intern_atom(data->connection, true, (uint16_t) strlen("EDID"), "EDID");
- FF_AUTO_FREE xcb_intern_atom_reply_t* requestAtomReply = data->ffxcb_intern_atom_reply(data->connection, requestAtomCookie, NULL);
- FF_AUTO_FREE xcb_randr_get_output_property_reply_t* outputPropertyReply = NULL;
+ FF_AUTO_FREE xcb_randr_get_output_property_reply_t* edidReply = xcbRandrGetProperty(data, output, "EDID");
uint8_t* edidData = NULL;
uint32_t edidLength = 0;
- if(requestAtomReply)
+ if(edidReply)
{
- xcb_randr_get_output_property_cookie_t outputPropertyCookie = data->ffxcb_randr_get_output_property(data->connection, output, requestAtomReply->atom, XCB_GET_PROPERTY_TYPE_ANY, 0, 100, false, false);
- outputPropertyReply = data->ffxcb_randr_get_output_property_reply(data->connection, outputPropertyCookie, NULL);
- if(outputPropertyReply)
+ int len = data->ffxcb_randr_get_output_property_data_length(edidReply);
+ if(len >= 128)
{
- int len = data->ffxcb_randr_get_output_property_data_length(outputPropertyReply);
- if(len >= 128)
- {
- ffStrbufClear(name);
- edidData = data->ffxcb_randr_get_output_property_data(outputPropertyReply);
- ffEdidGetName(edidData, name);
- edidLength = (uint32_t) len;
- }
+ edidData = data->ffxcb_randr_get_output_property_data(edidReply);
+ edidLength = (uint32_t) len;
}
}
+ if(edidData)
+ {
+ ffStrbufClear(name);
+ ffEdidGetName(edidData, name);
+ }
+
+ uint8_t randrEmulation = 0;
+ 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];
+ }
+
xcb_randr_get_crtc_info_cookie_t crtcInfoCookie = data->ffxcb_randr_get_crtc_info(data->connection, outputInfoReply->crtc, XCB_CURRENT_TIME);
FF_AUTO_FREE xcb_randr_get_crtc_info_reply_t* crtcInfoReply = data->ffxcb_randr_get_crtc_info_reply(data->connection, crtcInfoCookie, NULL);
if(crtcInfoReply == NULL)
@@ -204,9 +223,12 @@ static bool xcbRandrHandleOutput(XcbRandrData* data, xcb_randr_output_t output,
0,
(uint32_t) outputInfoReply->mm_width,
(uint32_t) outputInfoReply->mm_height,
- currentMode ? "xcb-randr-mode" : "xcb-randr-crtc"
+ randrEmulation
+ ? (currentMode ? "xcb-randr-emu-mode" : "xcb-randr-emu-crtc")
+ : (currentMode ? "xcb-randr-mode" : "xcb-randr-crtc")
+
);
- if (item && edidLength)
+ if (item && 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);
diff --git a/src/detection/displayserver/linux/xlib.c b/src/detection/displayserver/linux/xlib.c
index 8820ad9355..eb407f7b5b 100644
--- a/src/detection/displayserver/linux/xlib.c
+++ b/src/detection/displayserver/linux/xlib.c
@@ -48,6 +48,32 @@ static unsigned char* x11GetProperty(XrandrData* data, Display* display, Window
return result;
}
+static uint8_t* xrandrGetProperty(XrandrData* data, RROutput output, const char* name, uint32_t* bufSize)
+{
+ unsigned long size = 0;
+ uint8_t* result = NULL;
+ Atom atomEdid = data->ffXInternAtom(data->display, name, true);
+ if (atomEdid != None)
+ {
+ int actual_format = 0;
+ unsigned long bytes_after = 0;
+ Atom actual_type = None;
+ if (data->ffXRRGetOutputProperty(data->display, output, atomEdid, 0, 100, false, false, AnyPropertyType, &actual_type, &actual_format, &size, &bytes_after, &result) == Success)
+ {
+ if (size == 0)
+ data->ffXFree(result);
+ else
+ {
+ if (bufSize)
+ *bufSize = (uint32_t) size;
+ return result;
+ }
+ }
+ }
+
+ return NULL;
+}
+
static void x11DetectWMFromEWMH(XrandrData* data, FFDisplayServerResult* result)
{
if(result->wmProcessName.length > 0 || ffStrbufCompS(&result->wmProtocolName, FF_WM_PROTOCOL_WAYLAND) == 0)
@@ -75,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)
+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)
{
//We do the check here, because we want the best fallback display if this call failed
if(screenResources == NULL)
@@ -131,7 +157,9 @@ static bool xrandrHandleCrtc(XrandrData* data, XRROutputInfo* output, FFstrbuf*
0,
(uint32_t) output->mm_width,
(uint32_t) output->mm_height,
- currentMode ? "xlib-randr-mode" : "xlib-randr-crtc"
+ randrEmulation
+ ? (currentMode ? "xlib-randr-emu-mode" : "xlib-randr-emu-crtc")
+ : (currentMode ? "xlib-randr-mode" : "xlib-randr-crtc")
);
if (item)
@@ -154,30 +182,25 @@ static bool xrandrHandleOutput(XrandrData* data, RROutput output, FFstrbuf* name
if(outputInfo == NULL)
return false;
- uint8_t* edidData = NULL;
- unsigned long edidLength = 0;
- Atom atomEdid = data->ffXInternAtom(data->display, "EDID", true);
- if (atomEdid != None)
+ uint32_t edidLength = 0;
+ uint8_t* edidData = xrandrGetProperty(data, output, RR_PROPERTY_RANDR_EDID, &edidLength);
+
+ if (edidLength >= 128)
{
- int actual_format = 0;
- unsigned long bytes_after = 0;
- Atom actual_type = None;
- if (data->ffXRRGetOutputProperty(data->display, output, atomEdid, 0, 100, false, false, AnyPropertyType, &actual_type, &actual_format, &edidLength, &bytes_after, &edidData) == Success)
- {
- if (edidLength >= 128)
- {
- ffStrbufClear(name);
- ffEdidGetName(edidData, name);
- }
- else
- edidLength = 0;
- }
+ ffStrbufClear(name);
+ ffEdidGetName(edidData, name);
}
+ else
+ edidLength = 0;
+
+ uint8_t* randrEmulation = xrandrGetProperty(data, output, "RANDR Emulation", NULL);
- bool res = xrandrHandleCrtc(data, outputInfo, name, primary, displayType, edidData, (uint32_t) edidLength, screenResources, bitDepth, scaleFactor);
+ bool res = xrandrHandleCrtc(data, outputInfo, name, primary, displayType, edidData, edidLength, screenResources, bitDepth, scaleFactor, randrEmulation ? !!randrEmulation[0] : false);
if (edidData)
data->ffXFree(edidData);
+ if (randrEmulation)
+ data->ffXFree(randrEmulation);
data->ffXRRFreeOutputInfo(outputInfo);
return res;
diff --git a/src/detection/editor/editor.c b/src/detection/editor/editor.c
index d8221f42f3..576938d47c 100644
--- a/src/detection/editor/editor.c
+++ b/src/detection/editor/editor.c
@@ -115,7 +115,8 @@ const char* ffDetectEditor(FFEditorResult* result)
ffStrbufEqualS(&result->exe, "hx") ||
ffStrbufEqualS(&result->exe, "code") ||
ffStrbufEqualS(&result->exe, "pluma") ||
- ffStrbufEqualS(&result->exe, "sublime_text")
+ ffStrbufEqualS(&result->exe, "sublime_text") ||
+ ffStrbufEqualS(&result->exe, "zeditor")
) param = "--version";
else if (
ffStrbufEqualS(&result->exe, "kak") ||
diff --git a/src/detection/gpu/gpu_windows.c b/src/detection/gpu/gpu_windows.c
index 853cb5217f..dfb24d8fff 100644
--- a/src/detection/gpu/gpu_windows.c
+++ b/src/detection/gpu/gpu_windows.c
@@ -352,11 +352,11 @@ const char* ffDetectGPUImpl(FF_MAYBE_UNUSED const FFGPUOptions* options, FFlist*
D3DKMT_ADAPTERTYPE adapterType = {};
D3DKMT_QUERYADAPTERINFO queryAdapterInfo = {
.hAdapter = openAdapterFromLuid.hAdapter,
- .Type = KMTQAITYPE_ADAPTERTYPE,
+ .Type = KMTQAITYPE_ADAPTERTYPE, // Windows 8 and later
.pPrivateDriverData = &adapterType,
.PrivateDriverDataSize = sizeof(adapterType),
};
- if (NT_SUCCESS(D3DKMTQueryAdapterInfo(&queryAdapterInfo))) // Vista and later
+ if (NT_SUCCESS(D3DKMTQueryAdapterInfo(&queryAdapterInfo)))
{
FF_DEBUG("Queried adapter type - HybridDiscrete: %d, HybridIntegrated: %d", adapterType.HybridDiscrete, adapterType.HybridIntegrated);
if (adapterType.HybridDiscrete)
@@ -369,7 +369,7 @@ const char* ffDetectGPUImpl(FF_MAYBE_UNUSED const FFGPUOptions* options, FFlist*
FF_DEBUG("Failed to query adapter type");
}
- if (gpu->frequency == FF_GPU_FREQUENCY_UNSET)
+ if (gpu->frequency == FF_GPU_FREQUENCY_UNSET && ffIsWindows11OrGreater())
{
FF_DEBUG("Trying to get GPU frequency information");
for (ULONG nodeIdx = 0; ; nodeIdx++)
@@ -379,14 +379,14 @@ const char* ffDetectGPUImpl(FF_MAYBE_UNUSED const FFGPUOptions* options, FFlist*
};
queryAdapterInfo = (D3DKMT_QUERYADAPTERINFO) {
.hAdapter = openAdapterFromLuid.hAdapter,
- .Type = KMTQAITYPE_NODEMETADATA,
+ .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; // Windows 10 and later
+ break;
}
if (nodeMetadata.NodeData.EngineType != DXGK_ENGINE_TYPE_3D)
{
@@ -395,11 +395,11 @@ const char* ffDetectGPUImpl(FF_MAYBE_UNUSED const FFGPUOptions* options, FFlist*
}
D3DKMT_QUERYSTATISTICS queryStatistics = {
- .Type = D3DKMT_QUERYSTATISTICS_NODE2,
+ .Type = D3DKMT_QUERYSTATISTICS_NODE2, // Windows 11 (22H2) and later
.AdapterLuid = *(LUID*)&adapterLuid,
.QueryNode2 = { .PhysicalAdapterIndex = 0, .NodeOrdinal = (UINT16) nodeIdx },
};
- if (NT_SUCCESS(D3DKMTQueryStatistics(&queryStatistics))) // Windows 11 (22H2) and later
+ 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);
@@ -422,11 +422,11 @@ const char* ffDetectGPUImpl(FF_MAYBE_UNUSED const FFGPUOptions* options, FFlist*
FF_DEBUG("Failed to open adapter from LUID");
}
- if (options->temp && gpu->temperature == FF_GPU_TEMP_UNSET)
+ 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,
+ .Type = D3DKMT_QUERYSTATISTICS_PHYSICAL_ADAPTER, // Windows 10 (1803) and later
.AdapterLuid = *(LUID*)&adapterLuid,
.QueryPhysAdapter = { .PhysicalAdapterIndex = 0 },
};
diff --git a/src/detection/keyboard/keyboard_linux.c b/src/detection/keyboard/keyboard_linux.c
index 8d1c9ed20b..9492710b58 100644
--- a/src/detection/keyboard/keyboard_linux.c
+++ b/src/detection/keyboard/keyboard_linux.c
@@ -19,9 +19,9 @@ const char* ffDetectKeyboard(FFlist* devices /* List of FFKeyboardDevice */)
if (!ffStrEndsWith(entry->d_name, "-event-kbd"))
continue;
- char buffer[32]; // `../eventX`
+ char buffer[32]; // `../eventXX`
ssize_t len = readlinkat(dirfd(dirp), entry->d_name, buffer, ARRAY_SIZE(buffer) - 1);
- if (len != strlen("../eventX") || !ffStrStartsWith(buffer, "../event")) continue;
+ if (len <= (ssize_t) strlen("../event") || !ffStrStartsWith(buffer, "../event")) continue;
buffer[len] = 0;
const char* eventid = buffer + strlen("../event");
@@ -30,8 +30,8 @@ const char* ffDetectKeyboard(FFlist* devices /* List of FFKeyboardDevice */)
uint32_t index = (uint32_t) strtoul(eventid, &pend, 10);
if (pend == eventid) continue;
- // Ignore duplicate entries
- if (flags & (1UL << index))
+ // Ignore duplicate entries (flags supports up to 64 event indices)
+ if (index >= 64 || (flags & (1UL << index)))
continue;
flags |= (1UL << index);
@@ -41,7 +41,7 @@ const char* ffDetectKeyboard(FFlist* devices /* List of FFKeyboardDevice */)
if (ffAppendFileBuffer(path.chars, &name))
{
ffStrbufTrimRightSpace(&name);
- ffStrbufSubstrBefore(&path, path.length - 4);
+ ffStrbufSubstrBefore(&path, path.length - (uint32_t) strlen("name"));
FFKeyboardDevice* device = (FFKeyboardDevice*) ffListAdd(devices);
ffStrbufInitMove(&device->name, &name);
diff --git a/src/detection/keyboard/keyboard_windows.c b/src/detection/keyboard/keyboard_windows.c
index c02d1f5a1e..1853d72fbc 100644
--- a/src/detection/keyboard/keyboard_windows.c
+++ b/src/detection/keyboard/keyboard_windows.c
@@ -5,7 +5,6 @@
#include "common/mallocHelper.h"
#include "common/windows/unicode.h"
-#include
#include
#include
#include
diff --git a/src/detection/media/media_linux.c b/src/detection/media/media_linux.c
index e7d5f0ad02..67b434d4e9 100644
--- a/src/detection/media/media_linux.c
+++ b/src/detection/media/media_linux.c
@@ -180,6 +180,7 @@ static bool getBusProperties(FFDBusData* data, const char* busName, FFMediaResul
ffStrbufClear(&result->album);
ffStrbufClear(&result->url);
ffStrbufClear(&result->status);
+ data->lib->ffdbus_message_unref(reply);
return false;
}
}
@@ -261,7 +262,7 @@ static void getBestBus(FFDBusData* data, FFMediaResult* result)
static const char* getMedia(FFMediaResult* result)
{
- FFDBusData data;
+ FF_DBUS_AUTO_DESTROY_DATA FFDBusData data = {};
const char* error = ffDBusLoadData(DBUS_BUS_SESSION, &data);
if(error != NULL)
return error;
diff --git a/src/detection/mouse/mouse_windows.c b/src/detection/mouse/mouse_windows.c
index 73c6c79555..0bc9b35d25 100644
--- a/src/detection/mouse/mouse_windows.c
+++ b/src/detection/mouse/mouse_windows.c
@@ -5,7 +5,6 @@
#include "common/mallocHelper.h"
#include "common/windows/unicode.h"
-#include
#include
#include
#include
diff --git a/src/detection/opengl/opengl_shared.c b/src/detection/opengl/opengl_shared.c
index fa70abf748..a3ee920f3e 100644
--- a/src/detection/opengl/opengl_shared.c
+++ b/src/detection/opengl/opengl_shared.c
@@ -98,6 +98,9 @@ static const char* eglHandleSurface(FFOpenGLResult* result, EGLData* data, bool
const char* error = eglHandleContext(result, data);
FF_DEBUG("eglHandleContext() returns: %s", error ?: "success");
+ FF_DEBUG("Releasing current EGL context");
+ data->ffeglMakeCurrent(data->display, EGL_NO_SURFACE, EGL_NO_SURFACE, EGL_NO_CONTEXT);
+
FF_DEBUG("Destroying EGL context");
data->ffeglDestroyContext(data->display, data->context);
return error;
diff --git a/src/detection/os/os_linux.c b/src/detection/os/os_linux.c
index 14c06a7750..2e58c3e4e5 100644
--- a/src/detection/os/os_linux.c
+++ b/src/detection/os/os_linux.c
@@ -269,6 +269,15 @@ FF_MAYBE_UNUSED static bool detectDebianDerived(FFOSResult* result)
ffStrbufSetStatic(&result->prettyName, "TrueNAS Scale");
return true;
}
+ else if (ffPathExists("/usr/bin/emmabuntus_config.sh", FF_PATHTYPE_FILE))
+ {
+ // Emmabuntüs
+ ffStrbufSetStatic(&result->id, "emmabuntus");
+ ffStrbufSetStatic(&result->idLike, "debian");
+ ffStrbufSetStatic(&result->name, "Emmabuntüs");
+ getDebianVersion(result);
+ return true;
+ }
else
{
// Hack for MX Linux. See #847
diff --git a/src/detection/os/os_windows.cpp b/src/detection/os/os_windows.c
similarity index 75%
rename from src/detection/os/os_windows.cpp
rename to src/detection/os/os_windows.c
index d56cdd7b36..bdb979d309 100644
--- a/src/detection/os/os_windows.cpp
+++ b/src/detection/os/os_windows.c
@@ -1,31 +1,10 @@
-extern "C" {
#include "os.h"
#include "common/library.h"
#include "common/stringUtils.h"
#include "common/windows/registry.h"
-}
-#include "common/windows/unicode.hpp"
-#include "common/windows/wmi.hpp"
-
-static const char* getOsNameByWmi(FFstrbuf* osName)
-{
- FFWmiQuery query(L"SELECT Caption FROM Win32_OperatingSystem");
- if(!query)
- return "Query WMI service failed";
+#include "common/windows/unicode.h"
- if(FFWmiRecord record = query.next())
- {
- if(auto vtCaption = record.get(L"Caption"))
- {
- ffStrbufSetWSV(osName, vtCaption.get());
- ffStrbufTrimRight(osName, ' ');
- return NULL;
- }
- return "Get Caption failed";
- }
-
- return "No WMI result returned";
-}
+#include
PWSTR WINAPI BrandingFormatString(PCWSTR format);
@@ -45,24 +24,13 @@ static bool getCodeName(FFOSResult* os)
return true;
}
-static const char* getOsNameByWinbrand(FFstrbuf* osName)
+void ffDetectOSImpl(FFOSResult* os)
{
//https://dennisbabkin.com/blog/?t=how-to-tell-the-real-version-of-windows-your-app-is-running-on#ver_string
- FF_LIBRARY_LOAD_MESSAGE(winbrand, "winbrand" FF_LIBRARY_EXTENSION, 1);
- FF_LIBRARY_LOAD_SYMBOL_MESSAGE(winbrand, BrandingFormatString);
-
- const wchar_t* rawName = ffBrandingFormatString(L"%WINDOWS_LONG%");
- ffStrbufSetWS(osName, rawName);
+ const wchar_t* rawName = BrandingFormatString(L"%WINDOWS_LONG%");
+ ffStrbufSetWS(&os->variant, rawName);
GlobalFree((HGLOBAL)rawName);
- return NULL;
-}
-
-extern "C"
-void ffDetectOSImpl(FFOSResult* os)
-{
- if(getOsNameByWinbrand(&os->variant) && getOsNameByWmi(&os->variant))
- return;
-
+ ffStrbufSet(&os->prettyName, &os->variant);
ffStrbufTrimRight(&os->variant, ' ');
//WMI returns the "Microsoft" prefix while BrandingFormatString doesn't. Make them consistent.
diff --git a/src/detection/packages/packages.h b/src/detection/packages/packages.h
index 12e9879330..8b8a435d95 100644
--- a/src/detection/packages/packages.h
+++ b/src/detection/packages/packages.h
@@ -27,6 +27,7 @@ typedef struct FFPackagesResult
uint32_t lpkgbuild;
uint32_t macports;
uint32_t mport;
+ uint32_t moss;
uint32_t nixDefault;
uint32_t nixSystem;
uint32_t nixUser;
diff --git a/src/detection/packages/packages_linux.c b/src/detection/packages/packages_linux.c
index 07998bc91a..8d9c9b0beb 100644
--- a/src/detection/packages/packages_linux.c
+++ b/src/detection/packages/packages_linux.c
@@ -492,6 +492,7 @@ static void getPackageCounts(FFstrbuf* baseDir, FFPackagesResult* packageCounts,
if (!(options->disabled & FF_PACKAGES_FLAG_PACSTALL_BIT)) packageCounts->pacstall += getNumElements(baseDir, "/var/lib/pacstall/metadata", false);
if (!(options->disabled & FF_PACKAGES_FLAG_PISI_BIT)) packageCounts->pisi += getNumElements(baseDir, "/var/lib/pisi/package", true);
if (!(options->disabled & FF_PACKAGES_FLAG_PKGSRC_BIT)) packageCounts->pkgsrc += getNumElements(baseDir, "/usr/pkg/pkgdb", DT_DIR);
+ if (!(options->disabled & FF_PACKAGES_FLAG_MOSS_BIT)) packageCounts->moss += getSQLite3Int(baseDir, "/.moss/db/state", "SELECT COUNT(*) FROM state_selections WHERE state_id = (SELECT MAX(id) FROM state)", "moss");
}
static void getPackageCountsRegular(FFstrbuf* baseDir, FFPackagesResult* packageCounts, FFPackagesOptions* options)
diff --git a/src/detection/swap/swap_windows.c b/src/detection/swap/swap_windows.c
index 0909267ecf..ced111f6cb 100644
--- a/src/detection/swap/swap_windows.c
+++ b/src/detection/swap/swap_windows.c
@@ -1,5 +1,4 @@
#include "swap.h"
-#include "common/mallocHelper.h"
#include "common/windows/unicode.h"
#include
diff --git a/src/detection/terminalfont/terminalfont_windows.c b/src/detection/terminalfont/terminalfont_windows.c
index 9cf9945714..2d8c087c74 100644
--- a/src/detection/terminalfont/terminalfont_windows.c
+++ b/src/detection/terminalfont/terminalfont_windows.c
@@ -1,5 +1,6 @@
#include "common/library.h"
#include "common/io.h"
+#include "common/path.h"
#include "common/processing.h"
#include "common/properties.h"
#include "common/windows/unicode.h"
@@ -94,45 +95,52 @@ static void detectFromWindowsTerminal(const FFstrbuf* terminalExe, FFTerminalFon
FF_STRBUF_AUTO_DESTROY json = ffStrbufCreate();
const char* error = NULL;
- if(terminalExe && terminalExe->length > 0 && !ffStrbufEqualS(terminalExe, "Windows Terminal"))
+ if(terminalExe && ffIsAbsolutePath(terminalExe->chars))
{
- char jsonPath[MAX_PATH + 1];
- char* pathEnd = ffStrCopy(jsonPath, terminalExe->chars, ffStrbufLastIndexC(terminalExe, '\\') + 1);
- ffStrCopy(pathEnd, ".portable", ARRAY_SIZE(jsonPath) - (size_t) (pathEnd - jsonPath) - 1);
+ FF_STRBUF_AUTO_DESTROY jsonPath = ffStrbufCreateA(MAX_PATH);
+ ffStrbufAppendNS(&jsonPath, ffStrbufLastIndexC(terminalExe, '\\') + 1, terminalExe->chars);
+ ffStrbufAppendS(&jsonPath, ".portable");
- if(ffPathExists(jsonPath, FF_PATHTYPE_ANY))
+ if(ffPathExists(jsonPath.chars, FF_PATHTYPE_ANY))
{
- ffStrCopy(pathEnd, "settings\\settings.json", ARRAY_SIZE(jsonPath) - (size_t) (pathEnd - jsonPath) - 1);
- if(!ffAppendFileBuffer(jsonPath, &json))
+ ffStrbufSubstrBefore(&jsonPath, jsonPath.length - strlen(".portable"));
+ ffStrbufAppendS(&jsonPath, "settings\\settings.json");
+ if(!ffAppendFileBuffer(jsonPath.chars, &json))
error = "Error reading Windows Terminal portable settings JSON file";
}
- else if(SUCCEEDED(SHGetFolderPathA(NULL, CSIDL_LOCAL_APPDATA, NULL, 0, jsonPath)))
+ else
{
- size_t remaining = ARRAY_SIZE(jsonPath) - strlen(jsonPath) - 1;
- if(ffStrbufContainIgnCaseS(terminalExe, "_8wekyb3d8bbwe\\"))
+ PWSTR localAppDataW = NULL;
+ if(SUCCEEDED(SHGetKnownFolderPath(&FOLDERID_LocalAppData, KF_FLAG_DEFAULT, NULL, &localAppDataW)))
{
- // Microsoft Store version
- if(ffStrbufContainIgnCaseS(terminalExe, ".WindowsTerminalPreview_"))
+ ffStrbufSetWS(&jsonPath, localAppDataW);
+ CoTaskMemFree(localAppDataW);
+
+ if(ffStrbufContainIgnCaseS(terminalExe, "_8wekyb3d8bbwe\\"))
{
- // Preview version
- strncat(jsonPath, "\\Packages\\Microsoft.WindowsTerminalPreview_8wekyb3d8bbwe\\LocalState\\settings.json", remaining);
- if(!ffAppendFileBuffer(jsonPath, &json))
- error = "Error reading Windows Terminal Preview settings JSON file";
+ // Microsoft Store version
+ if(ffStrbufContainIgnCaseS(terminalExe, ".WindowsTerminalPreview_"))
+ {
+ // Preview version
+ ffStrbufAppendS(&jsonPath, "\\Packages\\Microsoft.WindowsTerminalPreview_8wekyb3d8bbwe\\LocalState\\settings.json");
+ if(!ffAppendFileBuffer(jsonPath.chars, &json))
+ error = "Error reading Windows Terminal Preview settings JSON file";
+ }
+ else
+ {
+ // Stable version
+ ffStrbufAppendS(&jsonPath, "\\Packages\\Microsoft.WindowsTerminal_8wekyb3d8bbwe\\LocalState\\settings.json");
+ if(!ffAppendFileBuffer(jsonPath.chars, &json))
+ error = "Error reading Windows Terminal settings JSON file";
+ }
}
else
{
- // Stable version
- strncat(jsonPath, "\\Packages\\Microsoft.WindowsTerminal_8wekyb3d8bbwe\\LocalState\\settings.json", remaining);
- if(!ffAppendFileBuffer(jsonPath, &json))
+ ffStrbufAppendS(&jsonPath, "\\Microsoft\\Windows Terminal\\settings.json");
+ if(!ffAppendFileBuffer(jsonPath.chars, &json))
error = "Error reading Windows Terminal settings JSON file";
}
}
- else
- {
- strncat(jsonPath, "\\Microsoft\\Windows Terminal\\settings.json", remaining);
- if(!ffAppendFileBuffer(jsonPath, &json))
- error = "Error reading Windows Terminal settings JSON file";
- }
}
}
diff --git a/src/detection/terminalshell/terminalshell_windows.c b/src/detection/terminalshell/terminalshell_windows.c
index b412d5800f..f180d2894d 100644
--- a/src/detection/terminalshell/terminalshell_windows.c
+++ b/src/detection/terminalshell/terminalshell_windows.c
@@ -22,26 +22,29 @@ bool fftsGetShellVersion(FFstrbuf* exe, const char* exeName, FFstrbuf* version);
static uint32_t getShellInfo(FFShellResult* result, uint32_t pid)
{
uint32_t ppid = 0;
+ bool gui = false;
- while (pid != 0 && ffProcessGetInfoWindows(pid, &ppid, &result->processName, &result->exe, &result->exeName, &result->exePath, NULL))
+ while (pid != 0 && ffProcessGetInfoWindows(pid, &ppid, &result->processName, &result->exe, &result->exeName, &result->exePath, &gui))
{
ffStrbufSet(&result->prettyName, &result->processName);
- if(ffStrbufEndsWithIgnCaseS(&result->prettyName, ".exe"))
+ if (ffStrbufEndsWithIgnCaseS(&result->prettyName, ".exe"))
ffStrbufSubstrBefore(&result->prettyName, result->prettyName.length - 4);
//Common programs that are between terminal and own process, but are not the shell
- if(
+ if (
+ !gui && (
ffStrbufIgnCaseEqualS(&result->prettyName, "sudo") ||
ffStrbufIgnCaseEqualS(&result->prettyName, "su") ||
ffStrbufIgnCaseEqualS(&result->prettyName, "gdb") ||
ffStrbufIgnCaseEqualS(&result->prettyName, "lldb") ||
+ ffStrbufIgnCaseEqualS(&result->prettyName, "lldb-dap") ||
ffStrbufIgnCaseEqualS(&result->prettyName, "python") || // python on windows generates shim executables
ffStrbufIgnCaseEqualS(&result->prettyName, "fastfetch") || // scoop warps the real binaries with a "shim" exe
ffStrbufIgnCaseEqualS(&result->prettyName, "flashfetch") ||
ffStrbufContainIgnCaseS(&result->prettyName, "debug") ||
ffStrbufContainIgnCaseS(&result->prettyName, "time") ||
- ffStrbufStartsWithIgnCaseS(&result->prettyName, "ConEmu") // https://github.com/fastfetch-cli/fastfetch/issues/488#issuecomment-1619982014
- ) {
+ ffStrbufStartsWithIgnCaseS(&result->prettyName, "ConEmuC") // https://github.com/fastfetch-cli/fastfetch/issues/488#issuecomment-1619982014
+ )) {
ffStrbufClear(&result->processName);
ffStrbufClear(&result->prettyName);
ffStrbufClear(&result->exe);
@@ -51,13 +54,18 @@ static uint32_t getShellInfo(FFShellResult* result, uint32_t pid)
}
result->pid = pid;
- result->ppid = ppid;
- if(ffStrbufIgnCaseEqualS(&result->prettyName, "explorer"))
+ if (gui)
{
- ffStrbufSetS(&result->prettyName, "Windows Explorer"); // Started without shell
+ // Started without shell
// In this case, terminal process will be created by fastfetch itself.
ppid = 0;
+ if (ffStrbufIgnCaseEqualS(&result->prettyName, "explorer"))
+ ffStrbufSetS(&result->prettyName, "Windows Explorer");
+ }
+ else
+ {
+ result->ppid = ppid;
}
break;
@@ -90,9 +98,11 @@ static void setShellInfoDetails(FFShellResult* result)
{
if(wcsncmp(module.szModule, L"clink_dll_", strlen("clink_dll_")) == 0)
{
- ffStrbufAppendS(&result->prettyName, " (with Clink ");
- ffGetFileVersion(module.szExePath, NULL, &result->prettyName);
- ffStrbufAppendC(&result->prettyName, ')');
+ FF_STRBUF_AUTO_DESTROY clinkVersion = ffStrbufCreate();
+ if (ffGetFileVersion(module.szExePath, NULL, &clinkVersion))
+ ffStrbufAppendF(&result->prettyName, " (with Clink %s)", clinkVersion.chars);
+ else
+ ffStrbufAppendS(&result->prettyName, " (with Clink)");
break;
}
}
@@ -250,11 +260,11 @@ static uint32_t getTerminalInfo(FFTerminalResult* result, uint32_t pid)
}
uint32_t ppid = 0;
- bool hasGui;
+ bool gui;
- while (pid != 0 && ffProcessGetInfoWindows(pid, &ppid, &result->processName, &result->exe, &result->exeName, &result->exePath, &hasGui))
+ while (pid != 0 && ffProcessGetInfoWindows(pid, &ppid, &result->processName, &result->exe, &result->exeName, &result->exePath, &gui))
{
- if(!hasGui || ffStrbufIgnCaseEqualS(&result->processName, "far.exe")) // Far includes GUI objects...
+ if (!gui)
{
//We are in nested shell
ffStrbufClear(&result->processName);
diff --git a/src/detection/uptime/uptime_windows.c b/src/detection/uptime/uptime_windows.c
index c5cd7245ad..c5a4d423f1 100644
--- a/src/detection/uptime/uptime_windows.c
+++ b/src/detection/uptime/uptime_windows.c
@@ -1,34 +1,18 @@
#include "uptime.h"
#include "common/time.h"
-#include "common/library.h"
-
-#include
-#include
+#include "common/windows/nt.h"
const char* ffDetectUptime(FFUptimeResult* result)
{
- #if FF_WIN7_COMPAT
- HMODULE hKernelBase = GetModuleHandleW(L"KernelBase.dll");
- if (__builtin_expect(!!hKernelBase, true))
- {
- FF_LIBRARY_LOAD_SYMBOL_LAZY(hKernelBase, QueryInterruptTime);
- if (ffQueryInterruptTime) // Windows 10 and later
- {
- uint64_t uptime;
- ffQueryInterruptTime(&uptime);
- result->uptime = uptime / 10000; // Convert from 100-nanosecond intervals to milliseconds
- goto ok;
- }
- }
-
- result->uptime = GetTickCount64();
-ok:
+ // GetInterruptTime with Win7 support
+ uint64_t interruptTime = ffKSystemTimeToUInt64(&SharedUserData->InterruptTime);
- #else
- uint64_t uptime;
- QueryInterruptTime(&uptime);
- result->uptime = uptime / 10000; // Convert from 100-nanosecond intervals to milliseconds
- #endif
+ result->uptime = interruptTime / 10000; // Convert from 100-nanosecond intervals to milliseconds
result->bootTime = ffTimeGetNow() - result->uptime;
+
+ // Alternatively, `NtQuerySystemInformation(SystemTimeOfDayInformation)` reports the boot time directly,
+ // whose result exactly equals what WMI `Win32_OperatingSystem` reports
+ // with much lower accuracy (0.5 seconds)
+
return NULL;
}
diff --git a/src/detection/wifi/wifi_linux.c b/src/detection/wifi/wifi_linux.c
index 62fd017710..00b02aa843 100644
--- a/src/detection/wifi/wifi_linux.c
+++ b/src/detection/wifi/wifi_linux.c
@@ -47,7 +47,7 @@ typedef enum {
static const char* detectWifiWithNm(FFWifiResult* item, FFstrbuf* buffer)
{
FF_DEBUG("Starting NetworkManager wifi detection for interface %s", item->inf.description.chars);
- FFDBusData dbus;
+ FF_DBUS_AUTO_DESTROY_DATA FFDBusData dbus = {};
const char* error = ffDBusLoadData(DBUS_BUS_SYSTEM, &dbus);
if(error)
{
@@ -254,6 +254,7 @@ static const char* detectWifiWithNm(FFWifiResult* item, FFstrbuf* buffer)
}
FF_DEBUG("NetworkManager wifi detection completed successfully");
+ dbus.lib->ffdbus_message_unref(reply);
return NULL;
}
#endif // FF_HAVE_DBUS
diff --git a/src/detection/wm/wm_apple.m b/src/detection/wm/wm_apple.m
index 2036cfd12c..bf40cc93fb 100644
--- a/src/detection/wm/wm_apple.m
+++ b/src/detection/wm/wm_apple.m
@@ -5,6 +5,7 @@
#include "common/stringUtils.h"
#include
+#include
#import
const char* ffDetectWMPlugin(FFstrbuf* pluginName)
@@ -20,9 +21,10 @@
for(size_t i = 0; i < length / sizeof(struct kinfo_proc); i++)
{
- if (processes[i].kp_eproc.e_ppid != 1) continue;
+ const struct kinfo_proc* proc = &processes[i];
+ if (proc->kp_eproc.e_ppid != 1) continue;
- const char* comm = processes[i].kp_proc.p_comm;
+ const char* comm = proc->kp_proc.p_comm;
if(
!ffStrEqualsIgnCase(comm, "spectacle") &&
@@ -34,6 +36,42 @@
!ffStrEqualsIgnCase(comm, "rectangle")
) continue;
+ if (instance.config.general.detectVersion)
+ {
+ char buf[PROC_PIDPATHINFO_MAXSIZE];
+ int length = proc_pidpath(proc->kp_proc.p_pid, buf, ARRAY_SIZE(buf) - strlen("Info.plist"));
+ if (length > 0)
+ {
+ char* lastSlash = strrchr(buf, '/');
+ if (lastSlash)
+ {
+ *lastSlash = '\0';
+ if (ffStrEndsWith(buf, ".app/Contents/MacOS"))
+ {
+ lastSlash -= strlen("MacOS");
+ strcpy(lastSlash, "Info.plist"); // X.app/Contents/Info.plist
+ NSError* error;
+ NSDictionary* dict = [NSDictionary dictionaryWithContentsOfURL:[NSURL fileURLWithPath:@(buf)]
+ error:&error];
+ if (dict)
+ {
+ NSString* name = dict[@"CFBundleDisplayName"] ?: dict[@"CFBundleName"];
+ ffStrbufSetS(pluginName, name.UTF8String ?: comm);
+
+ NSString* version = dict[@"CFBundleShortVersionString"];
+ if (version)
+ {
+ ffStrbufAppendC(pluginName, ' ');
+ ffStrbufAppendS(pluginName, version.UTF8String);
+ }
+
+ break;
+ }
+ }
+ }
+ }
+ }
+
ffStrbufAppendS(pluginName, comm);
pluginName->chars[0] = (char) toupper(pluginName->chars[0]);
break;
@@ -50,16 +88,16 @@
if (ffStrbufEqualS(wmName, "WindowServer"))
{
NSError* error;
- NSDictionary* dict = [NSDictionary dictionaryWithContentsOfURL:[NSURL URLWithString:@"file:///System/Library/PrivateFrameworks/SkyLight.framework/Resources/version.plist"]
+ NSDictionary* dict = [NSDictionary dictionaryWithContentsOfURL:[NSURL fileURLWithPath:@"/System/Library/PrivateFrameworks/SkyLight.framework/Resources/version.plist" isDirectory:NO]
error:&error];
if (!dict)
{
- dict = [NSDictionary dictionaryWithContentsOfURL:[NSURL URLWithString:@"file:///System/Library/Frameworks/ApplicationServices.framework/Frameworks/CoreGraphics.framework/Resources/version.plist"]
+ dict = [NSDictionary dictionaryWithContentsOfURL:[NSURL fileURLWithPath:@"/System/Library/Frameworks/ApplicationServices.framework/Frameworks/CoreGraphics.framework/Resources/version.plist" isDirectory:NO]
error:&error];
}
if (dict)
- ffStrbufInitS(result, ((NSString*) dict[@"CFBundleShortVersionString"]).UTF8String);
+ ffStrbufSetS(result, ((NSString*) dict[@"CFBundleShortVersionString"]).UTF8String);
}
return NULL;
diff --git a/src/detection/wm/wm_linux.c b/src/detection/wm/wm_linux.c
index 56d8926e8d..2547c1f63f 100644
--- a/src/detection/wm/wm_linux.c
+++ b/src/detection/wm/wm_linux.c
@@ -170,8 +170,8 @@ static const char* getNiri(FFstrbuf* result)
NULL
}) == NULL)
{ // niri 25.11 (b35bcae)
- ffStrbufSubstrBeforeLastC(result, ' ');
ffStrbufSubstrAfterFirstC(result, ' ');
+ ffStrbufSubstrBeforeLastC(result, ' ');
return NULL;
}
diff --git a/src/detection/wm/wm_windows.c b/src/detection/wm/wm_windows.c
new file mode 100644
index 0000000000..b1b6268c92
--- /dev/null
+++ b/src/detection/wm/wm_windows.c
@@ -0,0 +1,204 @@
+#include "wm.h"
+#include "common/mallocHelper.h"
+#include "common/io.h"
+#include "common/library.h"
+#include "common/processing.h"
+#include "common/windows/nt.h"
+#include "common/windows/unicode.h"
+#include "common/windows/version.h"
+
+#include
+#include
+#include
+#include
+#include
+
+typedef enum {
+ FF_PROCESS_TYPE_NONE,
+ FF_PROCESS_TYPE_SIGNED = 1 << 0,
+ FF_PROCESS_TYPE_WINDOWS_STORE = 1 << 1,
+ FF_PROCESS_TYPE_GUI = 1 << 2,
+ FF_PROCESS_TYPE_CUI = 1 << 3,
+} FFProcessType;
+
+static bool verifySignature(const wchar_t* filePath)
+{
+ FF_LIBRARY_LOAD(wintrust, true, "wintrust" FF_LIBRARY_EXTENSION, -1)
+ FF_LIBRARY_LOAD_SYMBOL(wintrust, WinVerifyTrustEx, true)
+
+ WINTRUST_FILE_INFO fileInfo = {
+ .cbStruct = sizeof(fileInfo),
+ .pcwszFilePath = filePath,
+ };
+
+ GUID actionID = WINTRUST_ACTION_GENERIC_VERIFY_V2;
+
+ WINTRUST_DATA trustData = {
+ .cbStruct = sizeof(trustData),
+ .dwUIChoice = WTD_UI_NONE,
+ .fdwRevocationChecks = WTD_REVOKE_NONE,
+ .dwUnionChoice = WTD_CHOICE_FILE,
+ .pFile = &fileInfo,
+ .dwStateAction = WTD_STATEACTION_VERIFY,
+ .dwProvFlags = WTD_SAFER_FLAG,
+ };
+
+ LONG status = ffWinVerifyTrustEx(NULL, &actionID, &trustData);
+ trustData.dwStateAction = WTD_STATEACTION_CLOSE;
+ ffWinVerifyTrustEx(NULL, &actionID, &trustData);
+
+ return status == ERROR_SUCCESS;
+}
+
+static bool isProcessTrusted(DWORD processId, FFProcessType processType, UNICODE_STRING* buffer, size_t bufSize)
+{
+ FF_AUTO_CLOSE_FD HANDLE hProcess = OpenProcess(PROCESS_QUERY_LIMITED_INFORMATION, FALSE, processId);
+ if (!hProcess)
+ return false;
+
+ ULONG size;
+ if(!NT_SUCCESS(NtQueryInformationProcess(hProcess, ProcessImageFileNameWin32, buffer, (ULONG) bufSize, &size)) ||
+ buffer->Length == 0) return false;
+ assert(buffer->MaximumLength >= buffer->Length + 2); // NULL terminated
+
+ if (processType & FF_PROCESS_TYPE_WINDOWS_STORE)
+ {
+ static wchar_t windowsAppsPath[MAX_PATH];
+ static uint32_t windowsAppsPathLen;
+ if (windowsAppsPathLen == 0)
+ {
+ PWSTR pPath = NULL;
+ if(SUCCEEDED(SHGetKnownFolderPath(&FOLDERID_ProgramFiles, KF_FLAG_DEFAULT, NULL, &pPath)))
+ {
+ windowsAppsPathLen = (uint32_t) wcslen(pPath);
+ memcpy(windowsAppsPath, pPath, windowsAppsPathLen * sizeof(wchar_t));
+ memcpy(windowsAppsPath + windowsAppsPathLen, L"\\WindowsApps\\", sizeof(L"\\WindowsApps\\"));
+ windowsAppsPathLen += strlen("\\WindowsApps\\");
+ }
+ else
+ {
+ windowsAppsPathLen = -1u;
+ }
+ CoTaskMemFree(pPath);
+ }
+ if (windowsAppsPathLen != -1u &&
+ (buffer->Length <= windowsAppsPathLen * sizeof(wchar_t) || // Path is too short to be in WindowsApps
+ _wcsnicmp(buffer->Buffer, windowsAppsPath, windowsAppsPathLen) != 0) // Path does not start with WindowsApps
+ ) return false;
+ }
+
+ if (processType & FF_PROCESS_TYPE_SIGNED)
+ {
+ if (!verifySignature(buffer->Buffer)) return false;
+ }
+
+ if (processType & (FF_PROCESS_TYPE_GUI | FF_PROCESS_TYPE_CUI))
+ {
+ SECTION_IMAGE_INFORMATION info = {};
+ if(!NT_SUCCESS(NtQueryInformationProcess(hProcess, ProcessImageInformation, &info, sizeof(info), &size)) ||
+ size != sizeof(info)) return false;
+
+ if ((processType & FF_PROCESS_TYPE_GUI) && info.SubSystemType != IMAGE_SUBSYSTEM_WINDOWS_GUI)
+ return false;
+ if ((processType & FF_PROCESS_TYPE_CUI) && info.SubSystemType != IMAGE_SUBSYSTEM_WINDOWS_CUI)
+ return false;
+ }
+
+ return true;
+}
+
+#define ffStrEqualNWS(str, compareTo) (_wcsnicmp(str, L ## compareTo, sizeof(compareTo) - 1) == 0)
+
+const char* ffDetectWMPlugin(FFstrbuf* pluginName)
+{
+ alignas(UNICODE_STRING) uint8_t buffer[4096];
+ UNICODE_STRING* filePath = (UNICODE_STRING*) buffer;
+ SYSTEM_PROCESS_INFORMATION* FF_AUTO_FREE pstart = NULL;
+
+ // Multiple attempts in case processes change while
+ // we are in the middle of querying them.
+ ULONG size = 0;
+ for (int attempts = 0;; ++attempts)
+ {
+ if (size)
+ {
+ pstart = (SYSTEM_PROCESS_INFORMATION*)realloc(pstart, size);
+ assert(pstart);
+ }
+ NTSTATUS status = NtQuerySystemInformation(SystemProcessInformation, pstart, size, &size);
+ if(NT_SUCCESS(status))
+ break;
+ else if(status == STATUS_INFO_LENGTH_MISMATCH && attempts < 4)
+ size += sizeof(SYSTEM_PROCESS_INFORMATION) * 5;
+ else
+ return "NtQuerySystemInformation(SystemProcessInformation) failed";
+ }
+
+ for (SYSTEM_PROCESS_INFORMATION* ptr = pstart; ; ptr = (SYSTEM_PROCESS_INFORMATION*)((uint8_t*)ptr + ptr->NextEntryOffset))
+ {
+ assert(ptr->ImageName.Length == 0 || ptr->ImageName.MaximumLength >= ptr->ImageName.Length + 2); // NULL terminated
+ if (ptr->ImageName.Length == strlen("FancyWM-GUI.exe") * sizeof(wchar_t) &&
+ ffStrEqualNWS(ptr->ImageName.Buffer, "FancyWM-GUI.exe") &&
+ isProcessTrusted((DWORD) (uintptr_t) ptr->UniqueProcessId, FF_PROCESS_TYPE_WINDOWS_STORE | FF_PROCESS_TYPE_GUI, filePath, sizeof(buffer))
+ ) {
+ if (instance.config.general.detectVersion && ffGetFileVersion(filePath->Buffer, NULL, pluginName))
+ ffStrbufPrependS(pluginName, "FancyWM ");
+ else
+ ffStrbufSetStatic(pluginName, "FancyWM");
+ break;
+ }
+ else if (ptr->ImageName.Length == strlen("glazewm-watcher.exe") * sizeof(wchar_t) &&
+ ffStrEqualNWS(ptr->ImageName.Buffer, "glazewm-watcher.exe") &&
+ isProcessTrusted((DWORD) (uintptr_t) ptr->UniqueProcessId, FF_PROCESS_TYPE_SIGNED | FF_PROCESS_TYPE_GUI, filePath, sizeof(buffer))
+ ) {
+ if (instance.config.general.detectVersion && ffGetFileVersion(filePath->Buffer, NULL, pluginName))
+ ffStrbufPrependS(pluginName, "GlazeWM ");
+ else
+ ffStrbufSetStatic(pluginName, "GlazeWM");
+ break;
+ }
+ else if (ptr->ImageName.Length == strlen("komorebi.exe") * sizeof(wchar_t) &&
+ ffStrEqualNWS(ptr->ImageName.Buffer, "komorebi.exe") &&
+ isProcessTrusted((DWORD) (uintptr_t) ptr->UniqueProcessId, FF_PROCESS_TYPE_CUI, filePath, sizeof(buffer))
+ ) {
+ if (instance.config.general.detectVersion)
+ {
+ FF_STRBUF_AUTO_DESTROY path = ffStrbufCreateNWS(filePath->Length / sizeof(wchar_t), filePath->Buffer);
+ if (ffProcessAppendStdOut(pluginName, (char *const[]) {
+ path.chars,
+ "--version",
+ NULL,
+ }) == NULL)
+ ffStrbufSubstrBeforeFirstC(pluginName, '\n');
+ }
+ if (pluginName->length == 0)
+ ffStrbufSetStatic(pluginName, "Komorebi");
+ break;
+ }
+
+ if (ptr->NextEntryOffset == 0) break;
+ }
+
+ return NULL;
+}
+
+const char* ffDetectWMVersion(const FFstrbuf* wmName, FFstrbuf* result, FF_MAYBE_UNUSED FFWMOptions* options)
+{
+ if (!wmName)
+ return "No WM detected";
+
+ if (ffStrbufEqualS(wmName, "dwm.exe"))
+ {
+ PWSTR pPath = NULL;
+ if(SUCCEEDED(SHGetKnownFolderPath(&FOLDERID_System, KF_FLAG_DEFAULT, NULL, &pPath)))
+ {
+ wchar_t fullPath[MAX_PATH];
+ wcscpy(fullPath, pPath);
+ wcscat(fullPath, L"\\dwm.exe");
+ ffGetFileVersion(fullPath, NULL, result);
+ }
+ CoTaskMemFree(pPath);
+ return NULL;
+ }
+ return "Not supported on this platform";
+}
diff --git a/src/detection/wm/wm_windows.cpp b/src/detection/wm/wm_windows.cpp
deleted file mode 100644
index 84ee1bde2f..0000000000
--- a/src/detection/wm/wm_windows.cpp
+++ /dev/null
@@ -1,79 +0,0 @@
-extern "C"
-{
-#include "wm.h"
-#include "common/io.h"
-#include "common/windows/version.h"
-#include "detection/terminalshell/terminalshell.h"
-}
-
-#include "common/windows/com.hpp"
-#include "common/windows/util.hpp"
-
-#include
-#include
-#include
-#include
-
-extern "C"
-const char* ffDetectWMPlugin(FFstrbuf* pluginName)
-{
- DWORD pid = ffDetectTerminal()->pid; // Whatever GUI program
- if (pid == 0) return "Unable to find a GUI program";
-
- FF_AUTO_CLOSE_FD HANDLE snapshot = NULL;
- while(!(snapshot = CreateToolhelp32Snapshot(TH32CS_SNAPMODULE, pid)) && GetLastError() == ERROR_BAD_LENGTH) {}
-
- if(!snapshot)
- return "CreateToolhelp32Snapshot(TH32CS_SNAPMODULE, pid) failed";
-
- if (ffInitCom())
- return "ffInitCom() failed";
-
- MODULEENTRY32W module;
- module.dwSize = sizeof(module);
- for(BOOL success = Module32FirstW(snapshot, &module); success; success = Module32NextW(snapshot, &module))
- {
- if(wcscmp(module.szModule, L"wbhelp64.dll") == 0 || wcscmp(module.szModule, L"wbhelp.dll") == 0)
- {
- IShellItem2* shellItem = NULL;
- if (FAILED(SHCreateItemFromParsingName(module.szExePath, NULL, IID_IShellItem2, (void**) &shellItem)))
- continue;
-
- on_scope_exit destroyShellItem([=] { shellItem->Release(); });
-
- wchar_t* desc = NULL;
- if (FAILED(shellItem->GetString(PKEY_FileDescription, &desc)))
- continue;
-
- on_scope_exit destroyDesc([=] { CoTaskMemFree(desc); });
-
- if (wcscmp(desc, L"WindowBlinds Helper DLL") == 0)
- {
- ffStrbufSetStatic(pluginName, "WindowBlinds");
- break;
- }
- }
- }
- return NULL;
-}
-
-const char* ffDetectWMVersion(const FFstrbuf* wmName, FFstrbuf* result, FF_MAYBE_UNUSED FFWMOptions* options)
-{
- if (!wmName)
- return "No WM detected";
-
- if (ffStrbufEqualS(wmName, "dwm.exe"))
- {
- PWSTR pPath = NULL;
- if(SUCCEEDED(SHGetKnownFolderPath(FOLDERID_System, KF_FLAG_DEFAULT, NULL, &pPath)))
- {
- wchar_t fullPath[MAX_PATH];
- wcscpy(fullPath, pPath);
- wcscat(fullPath, L"\\dwm.exe");
- ffGetFileVersion(fullPath, NULL, result);
- }
- CoTaskMemFree(pPath);
- return NULL;
- }
- return "Not supported on this platform";
-}
diff --git a/src/logo/ascii/artix2.txt b/src/logo/ascii/artix2.txt
new file mode 100644
index 0000000000..0e87beb2e4
--- /dev/null
+++ b/src/logo/ascii/artix2.txt
@@ -0,0 +1,20 @@
+ .:
+ .==:
+ .====:
+ .======:
+ .========:
+ .==========:
+ .============:
+ .-===========:
+ .:-========:
+ .-:. .:======:
+ .======:.. :-===.
+ .============:.. .:=.
+ .==================:..
+ .=======================:
+ .====================::. .
+ ================::. :-===
+ ============-:. .:========
+ ========-:. :-==========
+ =====:. ..:-=====
+ =::. ..::
diff --git a/src/logo/ascii/artix2_small.txt b/src/logo/ascii/artix2_small.txt
index 22459ee538..9f9052febf 100644
--- a/src/logo/ascii/artix2_small.txt
+++ b/src/logo/ascii/artix2_small.txt
@@ -1,13 +1,7 @@
- '
- 'A'
- 'ooo'
- 'ookxo'
- `ookxxo'
- '. `ooko'
- 'ooo`. `oo'
- 'ooxxxoo`. `'
- 'ookxxxkooo.` .
- 'ookxxkoo'` .'oo'
- 'ooxoo'` .:ooxxo'
- 'io'` `'oo'
-'` `'
\ No newline at end of file
+ /\
+ / \
+ /`'.,\
+ / ',
+ / ,`\
+ / ,.'`. \
+/.,'` `'.\
diff --git a/src/logo/ascii/artix_small.txt b/src/logo/ascii/artix_small.txt
index 7e016d82c3..14e70835aa 100644
--- a/src/logo/ascii/artix_small.txt
+++ b/src/logo/ascii/artix_small.txt
@@ -1,7 +1,13 @@
- /\
- / \
- /`'.,\
- / ',
- / ,`\
- / ,.'`. \
-/.,'` `'.\
\ No newline at end of file
+ '
+ 'A'
+ 'ooo'
+ 'ookxo'
+ `ookxxo'
+ '. `ooko'
+ 'ooo`. `oo'
+ 'ooxxxoo`. `'
+ 'ookxxxkooo.` .
+ 'ookxxkoo'` .'oo'
+ 'ooxoo'` .:ooxxo'
+ 'io'` `'oo'
+'` `'
diff --git a/src/logo/ascii/emmabuntus.txt b/src/logo/ascii/emmabuntus.txt
new file mode 100644
index 0000000000..8b5aa21c56
--- /dev/null
+++ b/src/logo/ascii/emmabuntus.txt
@@ -0,0 +1,15 @@
+ _~~_
+ nmmmmmmm/$2/**\$1\
+ nmHhHMMMHh\$2\__/$1/
+ nm zot $2__$1 t*~~*n
+ m b $2_+*´cc`*+_$1 p m
+ m & $2/%cc,;;,cc%\$1 & m
+ _~~_ & $2c__ +cc$1 & n
+/$2/**\$1\& $2cc;$1 & m
+\$2\__/$1/& $2c~~ +cc´$1 & n
+ *~~* & $2\cc%*--*%cc/$1 & m
+ m b $2`+.cccc.+´$1 p m
+ nm zo o_~~_
+ nmHhHMMMHhH/$2/**\$1\
+ nmmmmmmmm\$2\__/$1/
+ *~~*
diff --git a/src/logo/ascii/linuxmint.txt b/src/logo/ascii/linuxmint.txt
index ed7e6f4c6d..c6be56829b 100644
--- a/src/logo/ascii/linuxmint.txt
+++ b/src/logo/ascii/linuxmint.txt
@@ -1,19 +1,19 @@
- $2...-:::::-...
- .-MMMMMMMMMMMMMMM-.
- .-MMMM$1`..-:::::::-..`$2MMMM-.
- .:MMMM$1.:MMMMMMMMMMMMMMM:.$2MMMM:.
- -MMM$1-M---MMMMMMMMMMMMMMMMMMM.$2MMM-
- `:MMM$1:MM` :MMMM:....::-...-MMMM:$2MMM:`
- :MMM$1:MMM` :MM:` `` `` `:MMM:$2MMM:
-.MMM$1.MMMM` :MM. -MM. .MM- `MMMM.$2MMM.
-:MMM$1:MMMM` :MM. -MM- .MM: `MMMM-$2MMM:
-:MMM$1:MMMM` :MM. -MM- .MM: `MMMM:$2MMM:
-:MMM$1:MMMM` :MM. -MM- .MM: `MMMM-$2MMM:
-.MMM$1.MMMM` :MM:--:MM:--:MM: `MMMM.$2MMM.
- :MMM$1:MMM- `-MMMMMMMMMMMM-` -MMM-$2MMM:
- :MMM$1:MMM:` `:MMM:$2MMM:
- .MMM$1.MMMM:--------------:MMMM.$2MMM.
- '-MMMM$1.-MMMMMMMMMMMMMMM-.$2MMMM-'
- '.-MMMM$1``--:::::--``$2MMMM-.'
- '-MMMMMMMMMMMMM-'
- ``-:::::-``
\ No newline at end of file
+ $2_.-ppOOOOOOqq-._
+ .oOOOOPPPPPPPPPPOOOOo.
+ .oOOOO$1.=oOOOOOOOOOOo=.$2OOOOo.
+ .:OOO$1.=oOOOOOOOOOOOOOOOOo=.$2OOO:.
+ .OOO$1.OOOOOOOOOOOOOOOOOOOOOOOO.$2OOO.
+ .OOO$1.OO OOO:´ `::´ `:OOO.$2OO:
+ .OOO$1.OOO OO OOO.$2OOO:
+ OOO$1.OOOO OO oo oo OOOO.$2OOO
+:OOO$1:OOOO OO OO OO OOOO:$2OOO:
+:OOO$1:OOOO OO OO OO OOOO:$2OOO:
+'OOO$1'OOOO OO OO OO OOOO'$2OOO'
+ OOO$1'OOOO OO____OO____OO OOOO'$2OOO'
+ 'OOO$1'OOO 'OOOOOOOOOOOO' OOOO'$2OOO
+ 'OOO$1'OOO .OOO'$2OOO'
+ 'OOO$1'OOOO:ooooooooooooooo:OOOO'$2OOO'
+ ':OOOo$1'=OOOOOOOOOOOOOOOOO='$2oOOO:'
+ ':OOOOo$1'=OOOOOOOOOOO='$2oOOOO:'
+ ``-OOOOooooooooooOOOO-´´
+ ```-=:OOOO:=-´´´
diff --git a/src/logo/ascii/linuxmint2.txt b/src/logo/ascii/linuxmint2.txt
new file mode 100644
index 0000000000..c14451d3c4
--- /dev/null
+++ b/src/logo/ascii/linuxmint2.txt
@@ -0,0 +1,19 @@
+ $2...-:::::-...
+ .-MMMMMMMMMMMMMMM-.
+ .-MMMM$1`..-:::::::-..`$2MMMM-.
+ .:MMMM$1.:MMMMMMMMMMMMMMM:.$2MMMM:.
+ -MMM$1-M---MMMMMMMMMMMMMMMMMMM.$2MMM-
+ :MMM$1:MM` :MMMM:....::-...-MMMM:$2MMM:
+ :MMM$1:MMM` :MM:` `` `` `:MMM:$2MMM:
+.MMM$1.MMMM` :MM. -MM. .MM- `MMMM.$2MMM.
+:MMM$1:MMMM` :MM. -MM- .MM: `MMMM-$2MMM:
+:MMM$1:MMMM` :MM. -MM- .MM: `MMMM:$2MMM:
+:MMM$1:MMMM` :MM. -MM- .MM: `MMMM-$2MMM:
+.MMM$1.MMMM` :MM:--:MM:--:MM: `MMMM.$2MMM.
+ :MMM$1:MMM- `-MMMMMMMMMMMM-` -MMM-$2MMM:
+ :MMM$1:MMM:` `:MMM:$2MMM:
+ .MMM$1.MMMM:--------------:MMMM.$2MMM.
+ '-MMMM$1.-MMMMMMMMMMMMMMM-.$2MMMM-'
+ '.-MMMM$1``--:::::--``$2MMMM-.'
+ '-MMMMMMMMMMMMM-'
+ ``-:::::-``
\ No newline at end of file
diff --git a/src/logo/ascii/refracta.txt b/src/logo/ascii/refracta.txt
new file mode 100644
index 0000000000..6e8f484f15
--- /dev/null
+++ b/src/logo/ascii/refracta.txt
@@ -0,0 +1,18 @@
+ A
+ VW
+ VVW\
+ .yWWW\
+,;,,u,;yy;;v;uyyyyyyy ,WWWWW^
+ *WWWWWWWWWWWWWWWW/ $VWWWWw ,
+ ^*%WWWWWWVWWX $WWWW** ,yy
+ , "**WWW/' **' ,yy/WWW*`
+ &WWWWwy `*` <,ywWW%VWWW*
+ yWWWWWWWWWW* ., "**WW%W
+ ,&WWWWWM*"` ,y/ &WWWww ^*
+ XWWX*^ ,yWWWW09 .WWWWWWWWwy,
+*` &WWWWWM WWWWWWWWWWWWWww,
+ (WWWWW` /#####WWW***********
+ ^WWWW
+ VWW
+ Wh.
+ V/
\ No newline at end of file
diff --git a/src/logo/ascii/refracted_devuan.txt b/src/logo/ascii/refracted_devuan.txt
deleted file mode 100644
index 9e2ba7202c..0000000000
--- a/src/logo/ascii/refracted_devuan.txt
+++ /dev/null
@@ -1,18 +0,0 @@
- A
- VW
- VVW\
- .yWWW\
- ,;,,u,;yy;;v;uyyyyyyy ,WWWWW^
- *WWWWWWWWWWWWWWWW/ $VWWWWw ,
- ^*%WWWWWWVWWX $WWWW** ,yy
- , "**WWW/' **' ,yy/WWW*`
- &WWWWwy `*` <,ywWW%VWWW*
- yWWWWWWWWWW* ., "**WW%W
- ,&WWWWWM*"` ,y/ &WWWww ^*
- XWWX*^ ,yWWWW09 .WWWWWWWWwy,
- *` &WWWWWM WWWWWWWWWWWWWww,
- (WWWWW` /#####WWW***********
- ^WWWW
- VWW
- Wh.
- V/
\ No newline at end of file
diff --git a/src/logo/ascii/rengeos.txt b/src/logo/ascii/rengeos.txt
new file mode 100644
index 0000000000..995af0bdd9
--- /dev/null
+++ b/src/logo/ascii/rengeos.txt
@@ -0,0 +1,16 @@
+ ..
+ .:~!.
+ ...::^~!!7?J?:
+ ..::^^~~!!!7?JYYYYYYY?.
+ .::::..::^!7?JJYYYJYY!
+:~~~:.. .:~!?JJYYYYYYYJ!?J^
+ :!?JJ?7!!7?JYYYYYYYYYYYJ~.~?:
+ :~?YYYYYYYYYYYYYYY?~. ^7.
+ .~?YYYYYYYYYY?^ .~
+ ..:~!7?JYYYYYYYY?^
+.^~!7777777JYYYYY7:
+ ^YYYJ7:
+ :YYJ!:
+ :JJ!.
+ .7~.
+ ..
\ No newline at end of file
diff --git a/src/logo/ascii/secureblue.txt b/src/logo/ascii/secureblue.txt
index ce91b51b3b..859cdf160e 100644
--- a/src/logo/ascii/secureblue.txt
+++ b/src/logo/ascii/secureblue.txt
@@ -2,10 +2,10 @@
:========++++++++++++:
===============+++++++++++
====================++++++++++
- :=============$3#%@@%$1======++++++++-
- -============$3%@%+++*$3@@$1========+++++=
- -============$3%@#======$3@@$1==========+++-
- =============$3%@+======$3@@$1==============.
+ :=============$3#%@@@%$1=====++++++++-
+ -============$3%@%$1====$3%@@$1========+++++
+ -============$3%@#$1======$3@@$1==========+++-
+.=============$3%@+$1======$3@@$1==============.
$2--$1=========$3+@@@@@@@@@@@@@@@%+$1==========-
$2------$1=====$3%@@@@@@@@@@@@@@@@*$1===========
$2---------$1==$3%@@@@@@@%%@@@@@@@*$1===========
@@ -17,4 +17,4 @@ $2:----------$3%@@@@@#$1===$3+%@@@@@*$1==========-
$2-------------------------$1====-
$2--------------------------
.--------------------.
- ------------
\ No newline at end of file
+ ------------
diff --git a/src/logo/builtin.c b/src/logo/builtin.c
index c2f220778c..5315fdd45a 100644
--- a/src/logo/builtin.c
+++ b/src/logo/builtin.c
@@ -527,7 +527,7 @@ static const FFlogo A[] = {
},
// Artix
{
- .names = {"artix", "artixlinux", "artix-linux"},
+ .names = {"artix"},
.lines = FASTFETCH_DATATEXT_LOGO_ARTIX,
.colors = {
FF_COLOR_FG_CYAN,
@@ -537,7 +537,7 @@ static const FFlogo A[] = {
},
// ArtixSmall
{
- .names = {"artix_small", "artixlinux_small"},
+ .names = {"artix_small"},
.type = FF_LOGO_LINE_TYPE_SMALL_BIT,
.lines = FASTFETCH_DATATEXT_LOGO_ARTIX_SMALL,
.colors = {
@@ -548,7 +548,7 @@ static const FFlogo A[] = {
},
// Artix2Small
{
- .names = {"artix2_small", "artixlinux2_small"},
+ .names = {"artix2_small"},
.type = FF_LOGO_LINE_TYPE_SMALL_BIT | FF_LOGO_LINE_TYPE_ALTER_BIT,
.lines = FASTFETCH_DATATEXT_LOGO_ARTIX2_SMALL,
.colors = {
@@ -557,9 +557,9 @@ static const FFlogo A[] = {
.colorKeys = FF_COLOR_FG_CYAN,
.colorTitle = FF_COLOR_FG_CYAN,
},
- // ArcoLinux
+ // ArcoLinux (Discontinued)
{
- .names = {"arco", "arcolinux", "arco-linux"},
+ .names = {"arco", "arcolinux"}, // ID=arcolinux
.lines = FASTFETCH_DATATEXT_LOGO_ARCO,
.colors = {
FF_COLOR_FG_BLUE,
@@ -570,7 +570,7 @@ static const FFlogo A[] = {
},
// ArcoLinuxSmall
{
- .names = {"arco_small", "arcolinux_small", "arco-linux_small"},
+ .names = {"arco_small", "arcolinux_small"},
.type = FF_LOGO_LINE_TYPE_SMALL_BIT,
.lines = FASTFETCH_DATATEXT_LOGO_ARCO_SMALL,
.colors = {
@@ -1383,7 +1383,7 @@ static const FFlogo D[] = {
},
// Debian
{
- .names = {"Debian", "debian-linux"},
+ .names = {"Debian"},
.lines = FASTFETCH_DATATEXT_LOGO_DEBIAN,
.colors = {
FF_COLOR_FG_RED,
@@ -1394,7 +1394,7 @@ static const FFlogo D[] = {
},
// DebianSmall
{
- .names = {"Debian_small", "debian-linux_small"},
+ .names = {"Debian_small"},
.type = FF_LOGO_LINE_TYPE_SMALL_BIT,
.lines = FASTFETCH_DATATEXT_LOGO_DEBIAN_SMALL,
.colors = {
@@ -1403,9 +1403,9 @@ static const FFlogo D[] = {
.colorKeys = FF_COLOR_FG_RED,
.colorTitle = FF_COLOR_FG_RED,
},
- // deepin
+ // Deepin
{
- .names = {"deepin", "deepin-linux"},
+ .names = {"Deepin"},
.lines = FASTFETCH_DATATEXT_LOGO_DEEPIN,
.colors = {
FF_COLOR_FG_BLUE,
@@ -1573,6 +1573,15 @@ static const FFlogo E[] = {
.colorKeys = FF_COLOR_FG_DEFAULT,
.colorTitle = FF_COLOR_FG_CYAN,
},
+ // Emmabuntüs
+ {
+ .names = {"Emmabuntus"},
+ .lines = FASTFETCH_DATATEXT_LOGO_EMMABUNTUS,
+ .colors = {
+ FF_COLOR_FG_BLUE,
+ FF_COLOR_FG_YELLOW,
+ },
+ },
// EmperorOS
{
.names = {"Emperor"},
@@ -1699,7 +1708,7 @@ static const FFlogo E[] = {
},
// Exherbo
{
- .names = {"Exherbo", "exherbo-linux"},
+ .names = {"Exherbo"},
.lines = FASTFETCH_DATATEXT_LOGO_EXHERBO,
.colors = {
FF_COLOR_FG_BLUE,
@@ -1709,9 +1718,9 @@ static const FFlogo E[] = {
.colorKeys = FF_COLOR_FG_BLUE,
.colorTitle = FF_COLOR_FG_BLUE,
},
- // ExodiaPredator
+ // ExodiaOS
{
- .names = {"Exodia Predator", "exodia-predator", "Exodia Predator OS"},
+ .names = {"Exodia"},
.lines = FASTFETCH_DATATEXT_LOGO_EXODIA_PREDATOR,
.colors = {
FF_COLOR_FG_MAGENTA,
@@ -2747,9 +2756,9 @@ static const FFlogo L[] = {
.colorKeys = FF_COLOR_FG_DEFAULT,
.colorTitle = FF_COLOR_FG_DEFAULT,
},
- // LinuxLight
+ // LinuxLite
{
- .names = {"LinuxLite", "Linux Lite", "linux_lite"},
+ .names = {"LinuxLite"},
.lines = FASTFETCH_DATATEXT_LOGO_LINUXLITE,
.colors = {
FF_COLOR_FG_GREEN,
@@ -2758,9 +2767,9 @@ static const FFlogo L[] = {
.colorKeys = FF_COLOR_FG_GREEN,
.colorTitle = FF_COLOR_FG_DEFAULT,
},
- // LinuxLightSmall
+ // LinuxLiteSmall
{
- .names = {"LinuxLite_small", "Linux Lite_small"},
+ .names = {"LinuxLite_small"},
.type = FF_LOGO_LINE_TYPE_SMALL_BIT,
.lines = FASTFETCH_DATATEXT_LOGO_LINUXLITE_SMALL,
.colors = {
@@ -2772,7 +2781,7 @@ static const FFlogo L[] = {
},
// LinuxMint
{
- .names = {"linuxmint", "linux-mint"},
+ .names = {"linuxmint"},
.lines = FASTFETCH_DATATEXT_LOGO_LINUXMINT,
.colors = {
FF_COLOR_FG_GREEN,
@@ -2783,7 +2792,7 @@ static const FFlogo L[] = {
},
// LinuxMintSmall
{
- .names = {"linuxmint_small", "linux-mint_small"},
+ .names = {"linuxmint_small"},
.type = FF_LOGO_LINE_TYPE_SMALL_BIT,
.lines = FASTFETCH_DATATEXT_LOGO_LINUXMINT_SMALL,
.colors = {
@@ -2793,9 +2802,21 @@ static const FFlogo L[] = {
.colorKeys = FF_COLOR_FG_GREEN,
.colorTitle = FF_COLOR_FG_GREEN,
},
+ // LinuxMint2
+ {
+ .names = {"linuxmint2"},
+ .type = FF_LOGO_LINE_TYPE_ALTER_BIT,
+ .lines = FASTFETCH_DATATEXT_LOGO_LINUXMINT2,
+ .colors = {
+ FF_COLOR_FG_GREEN,
+ FF_COLOR_FG_WHITE,
+ },
+ .colorKeys = FF_COLOR_FG_GREEN,
+ .colorTitle = FF_COLOR_FG_GREEN,
+ },
// LinuxMintOld
{
- .names = {"linuxmint_old", "linux-mint_old"},
+ .names = {"linuxmint_old"},
.type = FF_LOGO_LINE_TYPE_ALTER_BIT,
.lines = FASTFETCH_DATATEXT_LOGO_LINUXMINT_OLD,
.colors = {
@@ -3004,7 +3025,7 @@ static const FFlogo M[] = {
},
// Magix
{
- .names = {"Magix","MagixOS"},
+ .names = {"Magix", "MagixOS"},
.lines = FASTFETCH_DATATEXT_LOGO_MAGIX,
.colors = {
FF_COLOR_FG_LIGHT_MAGENTA,
@@ -3060,7 +3081,7 @@ static const FFlogo M[] = {
},
// MassOS
{
- .names = {"MassOS", "mass"},
+ .names = {"MassOS"},
.lines = FASTFETCH_DATATEXT_LOGO_MASSOS,
.colors = {
FF_COLOR_FG_DEFAULT,
@@ -3177,7 +3198,7 @@ static const FFlogo M[] = {
},
// MiracleLinux
{
- .names = {"MIRACLE LINUX", "miracle_linux"},
+ .names = {"miraclelinux"},
.lines = FASTFETCH_DATATEXT_LOGO_MIRACLE_LINUX,
.colors = {
FF_COLOR_FG_256 "29",
@@ -3208,7 +3229,7 @@ static const FFlogo M[] = {
},
// MX
{
- .names = {"MX", "MX Linux"},
+ .names = {"MX"},
.lines = FASTFETCH_DATATEXT_LOGO_MX,
.colors = {
FF_COLOR_FG_DEFAULT,
@@ -3218,7 +3239,7 @@ static const FFlogo M[] = {
},
// MXSmall
{
- .names = {"MX_small", "mx linux_small"},
+ .names = {"MX_small"},
.type = FF_LOGO_LINE_TYPE_SMALL_BIT,
.lines = FASTFETCH_DATATEXT_LOGO_MX_SMALL,
.colors = {
@@ -3382,7 +3403,7 @@ static const FFlogo N[] = {
},
// Nobara
{
- .names = {"nobara", "nobara-linux"},
+ .names = {"nobara"},
.lines = FASTFETCH_DATATEXT_LOGO_NOBARA,
.colors = {
FF_COLOR_FG_DEFAULT,
@@ -4168,7 +4189,7 @@ static const FFlogo R[] = {
},
// Raspbian
{
- .names = {"raspbian", "raspi", "raspberrypi", "raspberrypios"},
+ .names = {"raspbian"},
.lines = FASTFETCH_DATATEXT_LOGO_RASPBIAN,
.colors = {
FF_COLOR_FG_RED,
@@ -4179,7 +4200,7 @@ static const FFlogo R[] = {
},
// RaspbianSmall
{
- .names = {"raspbian_small", "raspi_small", "raspberrypi_small", "raspberrypios_small"},
+ .names = {"raspbian_small"},
.type = FF_LOGO_LINE_TYPE_SMALL_BIT,
.lines = FASTFETCH_DATATEXT_LOGO_RASPBIAN_SMALL,
.colors = {
@@ -4263,7 +4284,7 @@ static const FFlogo R[] = {
},
// RedOS
{
- .names = {"RedOS", "RED OS", "red-os", "redos"},
+ .names = {"RedOS"},
.lines = FASTFETCH_DATATEXT_LOGO_REDOS,
.colors = {
FF_COLOR_FG_RED,
@@ -4274,7 +4295,7 @@ static const FFlogo R[] = {
},
// RedOS small
{
- .names = {"RedOS_small", "RED OS_small", "red-os_small", "redos_small"},
+ .names = {"RedOS_small"},
.type = FF_LOGO_LINE_TYPE_SMALL_BIT,
.lines = FASTFETCH_DATATEXT_LOGO_REDOS_SMALL,
.colors = {
@@ -4294,10 +4315,10 @@ static const FFlogo R[] = {
.colorKeys = FF_COLOR_FG_RED,
.colorTitle = FF_COLOR_FG_RED,
},
- // Refracted Devuan
+ // Refracta
{
- .names = {"Refracted Devuan", "refracted-devuan"},
- .lines = FASTFETCH_DATATEXT_LOGO_REFRACTED_DEVUAN,
+ .names = {"Refracta"},
+ .lines = FASTFETCH_DATATEXT_LOGO_REFRACTA,
.colors = {
FF_COLOR_FG_WHITE,
FF_COLOR_FG_LIGHT_BLACK,
@@ -4338,7 +4359,7 @@ static const FFlogo R[] = {
},
// RockyLinux
{
- .names = {"rocky", "rocky-linux", "rockylinux"},
+ .names = {"rocky"},
.lines = FASTFETCH_DATATEXT_LOGO_ROCKY,
.colors = {
FF_COLOR_FG_GREEN,
@@ -4348,7 +4369,7 @@ static const FFlogo R[] = {
},
// RockyLinuxSmall
{
- .names = {"rocky_small", "rocky-linux_small", "rockylinux_small"},
+ .names = {"rocky_small"},
.type = FF_LOGO_LINE_TYPE_SMALL_BIT,
.lines = FASTFETCH_DATATEXT_LOGO_ROCKY_SMALL,
.colors = {
@@ -4381,6 +4402,14 @@ static const FFlogo R[] = {
.colorKeys = FF_COLOR_FG_MAGENTA,
.colorTitle = FF_COLOR_FG_MAGENTA,
},
+ {
+ .names = {"RengeOS"},
+ .lines = FASTFETCH_DATATEXT_LOGO_RENGEOS,
+ .colors = {
+ FF_COLOR_FG_MAGENTA,
+ FF_COLOR_FG_MAGENTA,
+ },
+ },
// LAST
{},
};
diff --git a/src/modules/cpu/cpu.c b/src/modules/cpu/cpu.c
index 1e2e753787..5066d8515f 100644
--- a/src/modules/cpu/cpu.c
+++ b/src/modules/cpu/cpu.c
@@ -136,7 +136,7 @@ void ffParseCPUJsonObject(FFCPUOptions* options, yyjson_val* module)
if (unsafe_yyjson_equals_str(key, "tempSensor"))
{
- ffStrbufSetS(&options->tempSensor, unsafe_yyjson_get_str(val));
+ ffStrbufSetJsonVal(&options->tempSensor, val);
continue;
}
diff --git a/src/modules/packages/option.h b/src/modules/packages/option.h
index f39541388c..45c049710e 100644
--- a/src/modules/packages/option.h
+++ b/src/modules/packages/option.h
@@ -37,6 +37,7 @@ typedef enum __attribute__((__packed__)) FFPackagesFlags
FF_PACKAGES_FLAG_PISI_BIT = 1ULL << 29,
FF_PACKAGES_FLAG_SOAR_BIT = 1ULL << 30,
FF_PACKAGES_FLAG_KISS_BIT = 1ULL << 31,
+ FF_PACKAGES_FLAG_MOSS_BIT = 1ULL << 32,
FF_PACKAGES_FLAG_FORCE_UNSIGNED = UINT64_MAX,
} FFPackagesFlags;
static_assert(sizeof(FFPackagesFlags) == sizeof(uint64_t), "");
diff --git a/src/modules/packages/packages.c b/src/modules/packages/packages.c
index c9d3d55802..f94337182c 100644
--- a/src/modules/packages/packages.c
+++ b/src/modules/packages/packages.c
@@ -147,6 +147,7 @@ bool ffPrintPackages(FFPackagesOptions* options)
FF_PRINT_PACKAGE(mport)
FF_PRINT_PACKAGE(pisi)
FF_PRINT_PACKAGE(soar)
+ FF_PRINT_PACKAGE(moss)
putchar('\n');
}
@@ -196,6 +197,7 @@ bool ffPrintPackages(FFPackagesOptions* options)
FF_FORMAT_ARG(counts.pisi, "pisi"),
FF_FORMAT_ARG(counts.soar, "soar"),
FF_FORMAT_ARG(counts.kiss, "kiss"),
+ FF_FORMAT_ARG(counts.moss, "moss"),
FF_FORMAT_ARG(nixAll, "nix-all"),
FF_FORMAT_ARG(flatpakAll, "flatpak-all"),
FF_FORMAT_ARG(brewAll, "brew-all"),
@@ -281,6 +283,7 @@ void ffParsePackagesJsonObject(FFPackagesOptions* options, yyjson_val* module)
case 'M': if (false);
FF_TEST_PACKAGE_NAME(MACPORTS)
FF_TEST_PACKAGE_NAME(MPORT)
+ FF_TEST_PACKAGE_NAME(MOSS)
break;
case 'N': if (false);
FF_TEST_PACKAGE_NAME(NIX)
@@ -357,6 +360,7 @@ void ffGeneratePackagesJsonConfig(FFPackagesOptions* options, yyjson_mut_doc* do
FF_TEST_PACKAGE_NAME(LPKGBUILD)
FF_TEST_PACKAGE_NAME(MACPORTS)
FF_TEST_PACKAGE_NAME(MPORT)
+ FF_TEST_PACKAGE_NAME(MOSS)
FF_TEST_PACKAGE_NAME(NIX)
FF_TEST_PACKAGE_NAME(OPKG)
FF_TEST_PACKAGE_NAME(PACMAN)
@@ -412,8 +416,11 @@ bool ffGeneratePackagesJsonResult(FF_MAYBE_UNUSED FFPackagesOptions* options, yy
FF_APPEND_PACKAGE_COUNT(guixHome)
FF_APPEND_PACKAGE_COUNT(hpkgSystem)
FF_APPEND_PACKAGE_COUNT(hpkgUser)
+ FF_APPEND_PACKAGE_COUNT(kiss)
FF_APPEND_PACKAGE_COUNT(linglong)
+ FF_APPEND_PACKAGE_COUNT(macports)
FF_APPEND_PACKAGE_COUNT(mport)
+ FF_APPEND_PACKAGE_COUNT(moss)
FF_APPEND_PACKAGE_COUNT(nixDefault)
FF_APPEND_PACKAGE_COUNT(nixSystem)
FF_APPEND_PACKAGE_COUNT(nixUser)
@@ -425,13 +432,11 @@ bool ffGeneratePackagesJsonResult(FF_MAYBE_UNUSED FFPackagesOptions* options, yy
FF_APPEND_PACKAGE_COUNT(pkg)
FF_APPEND_PACKAGE_COUNT(pkgtool)
FF_APPEND_PACKAGE_COUNT(pkgsrc)
- FF_APPEND_PACKAGE_COUNT(macports)
FF_APPEND_PACKAGE_COUNT(rpm)
FF_APPEND_PACKAGE_COUNT(scoopUser)
FF_APPEND_PACKAGE_COUNT(scoopGlobal)
FF_APPEND_PACKAGE_COUNT(snap)
FF_APPEND_PACKAGE_COUNT(soar)
- FF_APPEND_PACKAGE_COUNT(kiss)
FF_APPEND_PACKAGE_COUNT(sorcery)
FF_APPEND_PACKAGE_COUNT(winget)
FF_APPEND_PACKAGE_COUNT(xbps)
@@ -506,6 +511,7 @@ FFModuleBaseInfo ffPackagesModuleInfo = {
{"Number of pisi packages", "pisi"},
{"Number of soar packages", "soar"},
{"Number of kiss packages", "kiss"},
+ {"Number of moss packages", "moss"},
{"Total number of all nix packages", "nix-all"},
{"Total number of all flatpak app packages", "flatpak-all"},
{"Total number of all brew packages", "brew-all"},
diff --git a/src/modules/title/title.c b/src/modules/title/title.c
index b3948429a3..89320a32c5 100644
--- a/src/modules/title/title.c
+++ b/src/modules/title/title.c
@@ -54,6 +54,22 @@ bool ffPrintTitle(FFTitleOptions* options)
}
else
{
+ FF_STRBUF_AUTO_DESTROY cwdTilde = ffStrbufCreate();
+ if (
+ #if _WIN32
+ ffStrbufStartsWithIgnCase
+ #else
+ ffStrbufStartsWith
+ #endif
+ (&instance.state.platform.cwd, &instance.state.platform.homeDir))
+ {
+ ffStrbufAppendS(&cwdTilde, "~/");
+ ffStrbufAppendNS(&cwdTilde, instance.state.platform.cwd.length - instance.state.platform.homeDir.length, &instance.state.platform.cwd.chars[instance.state.platform.homeDir.length]);
+ }
+ else
+ ffStrbufSet(&cwdTilde, &instance.state.platform.cwd);
+ if (cwdTilde.length > 1) ffStrbufTrimRight(&cwdTilde, '/');
+
FF_PRINT_FORMAT_CHECKED(FF_TITLE_MODULE_NAME, 0, &options->moduleArgs, FF_PRINT_TYPE_DEFAULT, ((FFformatarg[]){
FF_FORMAT_ARG(instance.state.platform.userName, "user-name"),
FF_FORMAT_ARG(hostName, "host-name"),
@@ -70,6 +86,7 @@ bool ffPrintTitle(FFTitleOptions* options)
FF_FORMAT_ARG(instance.state.platform.sid, "user-id"),
#endif
FF_FORMAT_ARG(instance.state.platform.pid, "pid"),
+ FF_FORMAT_ARG(cwdTilde, "cwd"),
}));
}
@@ -139,6 +156,7 @@ bool ffGenerateTitleJsonResult(FF_MAYBE_UNUSED FFTitleOptions* options, yyjson_m
yyjson_mut_obj_add_strbuf(doc, obj, "exePath", &instance.state.platform.exePath);
yyjson_mut_obj_add_strbuf(doc, obj, "userShell", &instance.state.platform.userShell);
yyjson_mut_obj_add_uint(doc, obj, "pid", instance.state.platform.pid);
+ yyjson_mut_obj_add_strbuf(doc, obj, "cwd", &instance.state.platform.cwd);
return true;
}
@@ -183,5 +201,6 @@ FFModuleBaseInfo ffTitleModuleInfo = {
{"Full user name", "full-user-name"},
{"UID (*nix) / SID (Windows)", "user-id"},
{"PID of current process", "pid"},
+ {"CWD with home dir replaced by `~`", "cwd"},
}))
};
diff --git a/src/modules/wm/wm.c b/src/modules/wm/wm.c
index fbfa6c10be..94a440bc5b 100644
--- a/src/modules/wm/wm.c
+++ b/src/modules/wm/wm.c
@@ -122,7 +122,7 @@ bool ffGenerateWMJsonResult(FF_MAYBE_UNUSED FFWMOptions* options, yyjson_mut_doc
void ffInitWMOptions(FFWMOptions* options)
{
ffOptionInitModuleArg(&options->moduleArgs, "");
- options->detectPlugin = false;
+ options->detectPlugin = true;
}
void ffDestroyWMOptions(FFWMOptions* options)