Compare commits

...

261 Commits

Author SHA1 Message Date
Carter Li e5d6006006 Merge pull request #2244 from fastfetch-cli/dev
Release: v2.61.0
2026-03-28 14:10:34 +08:00
李通洲 18f3809681 Display: rounds scaled size when calcuating
Follows 179539ccba
2026-03-28 13:01:16 +08:00
李通洲 1cd87dc994 GPU (Windows): silences a compiler warning [ci skip] 2026-03-28 12:52:33 +08:00
李通洲 1e58e04024 Doc: updates changelog [ci skip] 2026-03-28 12:49:23 +08:00
李通洲 408d939664 GPU (Windows/WSL): adds basic GPU type detection code 2026-03-28 12:11:29 +08:00
李通洲 9b1061719c Chore (OpenBSD): silences compiler warnings 2026-03-28 12:04:10 +08:00
李通洲 179539ccba Display: rounds scaled size when calcuating
Follows f50059dc7b
2026-03-28 08:50:15 +08:00
李通洲 6d73ecb944 CI: updates deps 2026-03-27 20:53:57 +08:00
李通洲 b981e4fd8b GPU (Windows): adds GPU type detection fallback with DXCore
Requires installation of `directx-headers`

Was using it in WSL -_-!!
2026-03-27 20:53:57 +08:00
李通洲 8e660136e0 GPU (WSL): increases maximum number of GPU adapters 2026-03-27 20:41:51 +08:00
李通洲 90ca39b464 Debug (Windows): adds ffDebugHResult 2026-03-27 20:14:47 +08:00
李通洲 63f7e15554 Sound (Windows): tidy 2026-03-27 20:14:21 +08:00
李通洲 ab37216f7d GPU (Windows): logs driver-specific detection error message 2026-03-27 15:37:33 +08:00
李通洲 57440a7a45 Global (Windows): makes NtWaitForSingleObject not alertable 2026-03-27 14:37:41 +08:00
李通洲 f50059dc7b Display: rounds scaled size when calculating 2026-03-27 14:35:29 +08:00
李通洲 92fb5cb0c1 GPU (WSL): supports VMEM total size query on Windows 10
Also adds debug logs
2026-03-27 13:33:38 +08:00
李通洲 5f84630276 CI (macOS): disables vulkan because it crashes on Intel build 2026-03-27 09:50:32 +08:00
李通洲 22696fe3de Common (Windows): fixes buf len handling 2026-03-26 23:41:17 +08:00
李通洲 cd00820546 FFstrbuf: adds a comment 2026-03-26 23:39:47 +08:00
李通洲 918153f974 Host (macOS): fixes typo 2026-03-26 23:39:31 +08:00
李通洲 58e67dcc7a Common (Windows): fixes potential out-of-bound mem access 2026-03-26 23:31:59 +08:00
李通洲 940d15c2e6 Doc: updates changelog [ci skip] 2026-03-26 23:17:03 +08:00
李通洲 9ffa46652d Common (Windows): remove default language attempt when querying file version 2026-03-26 23:08:28 +08:00
李通洲 d45dc53da0 GPU (OpenBSD): silences compiler warnings 2026-03-26 22:53:43 +08:00
李通洲 00d1812e8d Debug (Windows): let OS choose language to print 2026-03-26 22:53:28 +08:00
李通洲 50dc260fd6 Common (Windows): fixes str length variable handling 2026-03-26 20:27:57 +08:00
李通洲 bf99aeda92 Global: replaces %m for %s + strerror(errno)
Not portable

This reverts aabab6dff5
2026-03-26 19:54:10 +08:00
李通洲 397822d0db Global (Windows): resets console codepage on program exit
Fixes #2245
2026-03-26 18:22:00 +08:00
李通洲 12746a9681 Global: fixes issues raised by AI 2026-03-26 16:40:15 +08:00
李通洲 355e21c099 Release: v2.61.0 2026-03-26 15:23:51 +08:00
李通洲 9036b891a8 Host (macOS): adds new M5 models 2026-03-26 15:18:14 +08:00
李通洲 b7e4e91664 JsonSchema: regenerates moduleFormat 2026-03-26 15:01:31 +08:00
李通洲 aabab6dff5 Global (Linux): replaces strerror(errno) with %m 2026-03-26 14:55:46 +08:00
李通洲 62c2eb4d96 Networking (Linux): shutdown before recv 2026-03-26 14:47:33 +08:00
李通洲 c69a6b7640 Processing (Linux): reports command not found earlier 2026-03-26 14:23:12 +08:00
李通洲 0521513609 Sound (Windows): a small refactor 2026-03-25 16:17:20 +08:00
李通洲 a1c5fe2bf2 Processing (Windows): improves reliablity when terminating child process 2026-03-25 15:40:33 +08:00
李通洲 2d4a77b6f0 Networking (Windows): waits for completion of CancelIO 2026-03-25 15:03:23 +08:00
李通洲 9027f72353 Wifi (Linux): decodes SSID string printed by iw 2026-03-25 14:14:26 +08:00
李通洲 fe0f292cec FFstrbuf: adds new ffStrbufDecodeHexEscapeSequences 2026-03-25 14:13:03 +08:00
Carter Li 06ecd5a1ff Smbios (Linux): improve performance of small file reading 2026-03-25 11:05:36 +08:00
Carter Li 8fdaa987e3 IO: inlines more helper functions 2026-03-25 10:41:10 +08:00
李通洲 15804091b0 PhysicalDisk (Windows): detects tape devices 2026-03-24 18:42:56 +08:00
李通洲 d8b44a5eae Monitor: updates the description 2026-03-24 17:40:46 +08:00
李通洲 6357dcc6b4 README: adds comments 2026-03-24 17:40:22 +08:00
李通洲 947cddd926 Binary (Windows): fixes unmatched fn calls 2026-03-24 17:20:47 +08:00
李通洲 246ff0bc88 Common (Windows): adds a unique type for querying registry binary data 2026-03-24 17:20:47 +08:00
李通洲 339de56407 PhysicalMemory (macOS): don't set runningSpeed 2026-03-24 15:17:40 +08:00
Carter Li 795bbde459 Smbios: improves robostness; adds maiformed SMBIOS data verification and more debug log 2026-03-24 15:12:21 +08:00
李通洲 f32bf9e94f Bios (macOS): tidy 2026-03-24 15:12:21 +08:00
李通洲 8facb7c6e9 Uptime (Windows): improves comments 2026-03-23 16:01:29 +08:00
李通洲 92c65aad10 Font (Windows): reads Control Panel registry directly
Improves performance
2026-03-23 10:50:03 +08:00
李通洲 76d6e86fe5 Time (Windows): prefers RTL APIs 2026-03-23 10:03:12 +08:00
李通洲 e3d7eab4df Binary (Windows): prefers NT APIs 2026-03-23 09:54:04 +08:00
李通洲 ff8d4b46ef Icons (Windows): handles the case when users never changes the default settings 2026-03-22 20:21:11 +08:00
李通洲 4fb7860a8e Common (Windows): uses RtlOpenCurrentUser directly 2026-03-22 19:52:41 +08:00
李通洲 f188e9518d Colors: add brightness option for color display configuration
Fixes #2238
2026-03-22 13:43:16 +08:00
李通洲 a39419b12a Chore: tidy 2026-03-22 13:26:05 +08:00
李通洲 941502d718 Platform (Windows): fixes kernel version detection on Win 8 2026-03-21 23:19:45 +08:00
李通洲 de146c6370 Windows: removes 7/8 from manifest.xml 2026-03-21 23:19:23 +08:00
李通洲 09efb0d897 Host: fixes building on niche platforms 2026-03-21 22:39:42 +08:00
李通洲 8defffabff DisplayServer (Linux): fixes build failure on older Wayland versions
Fixes #2237
2026-03-21 18:56:23 +08:00
Carter Li 1b543b1eb8 PhysicalMemory: ignores custom SMBIOS entry
Fixes less memory devices are reported on certain devices
Found this issue on my old Intel MacBook Pro
2026-03-21 18:44:20 +08:00
Carter Li ab17fb08ef Global (macOS): queries SMBIOS data directly on Intel Mac 2026-03-21 18:38:01 +08:00
Carter Li a4bc3c812d SMBIOS: improves garbage data cleanup code 2026-03-21 18:27:48 +08:00
李通洲 c8fcabf4cc GPU (WSL): don't try to fetch info on unsupported systems 2026-03-21 11:04:07 +08:00
李通洲 a961a964ff GPU: adds more useful info 2026-03-21 11:03:32 +08:00
李通洲 412de625b2 GPU (Windows): detects used memory size by D3DKMT 2026-03-20 21:59:11 +08:00
李通洲 b739736527 GPU (WSL2): moves to D3DKMT/WDDM
Removes C++ dependency on Linux
2026-03-20 19:14:55 +08:00
李通洲 2b19f8078e GPU (Windows): prepares Linux support of d3dkmthk.h 2026-03-20 19:14:49 +08:00
李通洲 56eabbe2cb Brightness (Windows): queries monitor description if display name is not available 2026-03-19 18:07:24 +08:00
李通洲 b0215aae37 Common (Windows): fixes random failures of WMI initialization 2026-03-19 15:46:19 +08:00
李通洲 86cfa4eded Library (Windows): prefers Ldr APIs 2026-03-19 15:24:55 +08:00
李通洲 bab1e4ff82 Global (Windows): adds full TEB/PEB definitions 2026-03-19 15:16:50 +08:00
李通洲 6ec59f8654 Brightness (Windows): fixes detection of external monitors 2026-03-19 00:56:02 +08:00
李通洲 d3459af71f LocalIP: removes usage of htonl 2026-03-18 18:52:24 +08:00
李通洲 00b5f17f0d Global (Windows): prefers Rtl APIs 2026-03-18 18:36:41 +08:00
李通洲 b8ad833b4b Networking (Windows): prefers WSA functions 2026-03-18 18:35:59 +08:00
李通洲 fb412576e1 Host (macOS): updates M5 models 2026-03-18 13:11:07 +08:00
李通洲 d551651d63 WM (macOS): adds glazewm; removes unmaintained plugins 2026-03-18 10:56:09 +08:00
李通洲 4e234c52a3 Platform (Windows): adds implementation of POSIX readlink(2) and realpath(3) 2026-03-17 17:59:24 +08:00
李通洲 edc718d1d1 Locale: reports API failure messages 2026-03-17 17:33:02 +08:00
李通洲 42e01e7b8f Global (Windows): uses GetCurrentConsoleFontEx instead of GetCurrentConsoleFont 2026-03-16 15:16:54 +08:00
李通洲 8edfda979e Processing (Windows): prefers *W version APIs 2026-03-16 15:09:43 +08:00
李通洲 cc3595c3a4 Processing (Windows): prefers NT APIs 2026-03-16 14:41:52 +08:00
李通洲 8c72792e31 Common (Windows): adds a universal function ffFileTimeToUnixMs and use it 2026-03-16 13:49:49 +08:00
李通洲 f7ebeb58e2 Packages (Windows): prefers NT APIs 2026-03-16 10:50:30 +08:00
Carter Li ea4a4f0691 CPUCache (Linux): makes shared cache dedupe key boundary-safe
Fixes #2228
2026-03-16 09:52:43 +08:00
李通洲 81984e6e97 Global (Windows): prefers NtWaitForSingleObject 2026-03-14 23:36:59 +08:00
李通洲 f46e97fc62 Common (Windows): fixes querying HKEY_LOCAL_MACHINE on Windows 8.1
... and adds debug log
2026-03-14 22:57:29 +08:00
李通洲 7821552761 Common (Windows): adds ffDebugNtStatus for debugging 2026-03-14 22:55:16 +08:00
李通洲 cd8a0fc5f1 Global: enables debug mode when $FF_DEBUG is set
in case of `debugMode` is accessed before `--debug` is parsed
2026-03-14 22:27:36 +08:00
Carter Li 30f32f1fb5 CI (DFBSD): workarounds building errors 2026-03-14 21:52:55 +08:00
Carter Li 9af918b1ef DisplayServer (Linux): swaps width/height in vertical mode and native X11 2026-03-13 23:47:39 +08:00
李通洲 37b8bb4fc0 DisplayServer: reports DPI instead of scaled resolution
Fixes #2224
2026-03-13 20:44:21 +08:00
李通洲 66edea702e Common (Windows): reverts 42509f115c
Fixes warp terminal version detection
2026-03-13 13:58:50 +08:00
李通洲 930fb06ca7 TerminalFont (Windows): adds style detection support for warp terminal 2026-03-13 13:27:58 +08:00
李通洲 ca9c67353f Global (Windows): prefers NtClose over CloseHandle 2026-03-13 13:27:58 +08:00
李通洲 ea6ad4ac86 Global (Windows): prefers NTAPI for querying registry values 2026-03-13 13:27:58 +08:00
李通洲 6ed8aac6bb FFlist: adds ffListReserve 2026-03-13 09:52:18 +08:00
李通洲 cfabee4ba7 Common: adds ffEdidIsValid 2026-03-13 09:39:28 +08:00
李通洲 3a35f9735b Global: renames FFformatArgType to FFArgType for future reuse 2026-03-12 15:07:19 +08:00
Carter Li 05fa8b0994 CI (Linux): fixes a CI error
```
kde-output-device-v2-client-protocol.h:257:9: error: implicit declaration of function 'wl_proxy_marshal_flags'; did you mean 'wl_proxy_marshal_array'? [-Werror=implicit-function-declaration]
    257 |         wl_proxy_marshal_flags((struct wl_proxy *) kde_output_device_registry_v2,
        |         ^~~~~~~~~~~~~~~~~~~~~~
        |         wl_proxy_marshal_array
```
2026-03-12 10:06:00 +08:00
Carter Li 07f6f93415 Host (Linux): reports marketing product name on Asahi Linux 2026-03-12 10:01:40 +08:00
李通洲 d2de3a01bb IO (Windows): prefers native functions 2026-03-11 23:28:41 +08:00
李通洲 c9b7c65ba5 Brightness (Linux): prefers FF_LIBRARY_LOAD_SYMBOL_LAZY 2026-03-11 22:40:14 +08:00
李通洲 6ad8e68d26 Global: simplifies code with __auto_type 2026-03-11 22:40:14 +08:00
李通洲 b128260eb2 IO (Windows): improves sub directory creation logic 2026-03-11 22:40:13 +08:00
李通洲 ecfaf4d54d IO (Windows): fixes wrong va_list handling 2026-03-11 22:40:13 +08:00
李通洲 72bdee7fbf Global (Windows): prefers RtlUnicodeToUTF8N instead of WideCharToMultiByte 2026-03-11 22:40:13 +08:00
李通洲 bdb9593c0d Platform (Windows): prefers data in PEB 2026-03-11 22:40:13 +08:00
Carter Li 1e234754c2 PhysicalMemory (Linux): adds option showEmptySlots to show empty memory slots
Fixes #2222
2026-03-11 22:33:32 +08:00
Carter Li 4a4afdadb8 Keyboard (Linux): optimises detection code & reduces syscalls
Ref: #2220
2026-03-11 10:42:27 +08:00
Benny Powers 7cadece9a0 Keyboard (Linux): adds Bluetooth support (#2220)
* Keyboard (Linux) Bluetooth support

* fix: code review

* fix: code review

* fix: code review
2026-03-11 09:30:59 +08:00
李通洲 724ab6ba69 Platform (Windows): prefers info inside TEB/PEB 2026-03-09 20:25:10 +08:00
李通洲 6370485dcb Processing (Windows): prefers NT APIs 2026-03-09 20:24:19 +08:00
李通洲 60d343ca14 Platform (Windows): simplifies SID retrival 2026-03-09 20:19:47 +08:00
李通洲 bdc844f857 Bootmgr (Windows): prefers NT APIs 2026-03-09 15:20:01 +08:00
李通洲 d49c018f86 Common (Windows): updates nt.h 2026-03-09 15:19:36 +08:00
李通洲 be2201b5fc Platform (Windows): don't use instance.config here
... as it's not initialized yet
2026-03-07 23:50:31 +08:00
李通洲 9ba1cd1291 DisplayServer (Linux): updates kde-output-device-v2 again 2026-03-07 23:50:31 +08:00
butteronarchbtw 22fa9b51e8 WM (Linux): fixes version reporting for niri WM (#2218)
Signed-off-by: butteronarchbtw <152636161+butteronarchbtw@users.noreply.github.com>
2026-03-07 23:29:08 +08:00
李通洲 f781f8d9ea Global (Windows): drops WIN7_COMPAT support 2026-03-07 11:49:56 +08:00
Carter Li 06ecc9c480 Merge pull request #2217 from fastfetch-cli/dev
Release: v2.60.0
2026-03-06 21:08:19 -06:00
李通洲 50288d91e4 Doc: updates changelog [ci skip] 2026-03-07 10:43:16 +08:00
李通洲 499b4ff445 Kmod (Windows): don't specify maybe_unused on used params 2026-03-07 10:43:01 +08:00
李通洲 dd0021cd5e GPU (Windows): adds kernel version check before D3DKMT syscall 2026-03-07 10:34:21 +08:00
李通洲 f5b70dd8b7 Doc: updates changelog 2026-03-07 09:31:26 +08:00
李通洲 8f8ca11203 Kernel (Windows): simplifies kernel version detection 2026-03-07 09:26:13 +08:00
李通洲 121d399641 CPUUsage (Windows): simplifies Win10 detection 2026-03-07 09:24:38 +08:00
李通洲 26ae4eb21b Chore (Windows): removes unnecessary headers 2026-03-07 01:18:07 +08:00
李通洲 52a81dbdc4 Disk (Windows): reads SystemWindowsDirectory directly from SharedUserData 2026-03-07 00:35:07 +08:00
李通洲 f924a8bf06 Uptime: reads (System|Interrupt)Time directly from SharedUserData 2026-03-07 00:09:08 +08:00
李通洲 c7363f904a Common (Windows): adds definition of KUSER_SHARED_DATA 2026-03-06 23:59:19 +08:00
李通洲 53ffe8459f Global: applies Copilot's suggestions 2026-03-06 18:33:29 +08:00
李通洲 1b702e123e Release: v2.60.0 2026-03-06 15:27:55 +08:00
李通洲 14dccce393 CI (Linux): builds fastfetch with chafa support 2026-03-06 15:27:55 +08:00
李通洲 d040cc810f SMBIOS (Windows): fixes issues raised by Copilot 2026-03-06 14:46:51 +08:00
李通洲 224bea2559 WM (Windows): fixes issues raised by Copilot 2026-03-06 14:36:40 +08:00
李通洲 777d3e599d WM (macOS): fixes issues raised by Copilot 2026-03-06 14:09:13 +08:00
李通洲 7c47bc2656 WM: honors general.detectVersion 2026-03-06 11:10:35 +08:00
李通洲 e4d31381d8 WM (macOS): fetches WM plugin version 2026-03-06 11:08:41 +08:00
Dariqq abf8d75454 Chore (Windows): adds missing header (#2215) 2026-03-05 18:39:03 -06:00
李通洲 2ce152beaf SmbiosHelper (Windows): prefers NtQuerySystemInformation 2026-03-05 10:18:18 +08:00
李通洲 766ed07999 Refactor: prefers ffStr* 2026-03-05 10:16:24 +08:00
李通洲 ddc2131343 Chore (Windows): adds comments 2026-03-05 10:16:11 +08:00
Carter Li b64d8a013d DisplayServer (Linux): check if RANDR is running in Emulation mode 2026-03-05 10:12:38 +08:00
Carter Li e6db59fdde WM (Linux): fix niri version detection logic 2026-03-05 08:45:56 +08:00
李通洲 7ea0bbfb21 CPU / CPUCache (Windows): prefers NtQuerySystemInformation(Ex) 2026-03-03 16:01:36 +08:00
李通洲 ab041e0ad2 JsonSchema: update [ci skip] 2026-03-03 14:34:45 +08:00
李通洲 207f742624 TerminalFont (Windows): hardens the Windows Terminal font detection logic 2026-03-03 14:21:19 +08:00
李通洲 68f0b11b6f WM (Windows): loads wintrust lazily 2026-03-03 11:00:41 +08:00
李通洲 b86c0880b9 Bluetooth (Windows): adds a string variant helper; removes propsys dependency 2026-03-03 11:00:37 +08:00
李通洲 75f4a3d92d Platform (Windows): don't error out on i686 2026-03-03 08:47:34 +08:00
李通洲 70fa1341a8 Bootmgr (Windows): prefers NT API 2026-03-03 08:47:21 +08:00
李通洲 0a55314d37 WM (Windows): uses WinVerifyTrustEx consistantly 2026-03-03 00:27:28 +08:00
李通洲 2198e6123e DisplayServer (Windows): uses the standard GetDpiForMonitor to query scale factors 2026-03-03 00:23:36 +08:00
李通洲 dd37ee0e50 Doc: updates README [ci skip] 2026-03-01 19:27:33 +08:00
李通洲 7006ae0420 CMake (Windows): disable ENABLE_WIN7_COMPAT by default 2026-03-01 19:12:35 +08:00
李通洲 86458ab32a Kmod (SunOS): adds support 2026-03-01 14:44:29 +08:00
李通洲 67ca907e38 Kmod (Windows): adds support 2026-03-01 14:06:54 +08:00
李通洲 e15d06bbad Chore (Windows): move NT stuff to common 2026-03-01 13:58:38 +08:00
zamhedonia eb53797174 Logo (Builtin): adds support for Emmabuntüs (#2207)
* Logo (Builtin): adds Emmabuntüs Logo

* Add Detection for Emmabuntüs

* Update ASCII art in emmabuntus.txt

* Remove duplicate name for Emmabuntus logo

* Update emmabuntus.txt

* Update colors for Emmabuntus logo

---------

Co-authored-by: zamhedonia <zamhedonia@gmx.de>
Co-authored-by: Carter Li <CarterLi@users.noreply.github.com>
2026-03-01 00:13:16 +08:00
李通洲 c8c32a78b0 Presets: adds examples/32 2026-02-28 18:51:36 +08:00
李通洲 1c032624ac Title: adds {cwd-tilde} support 2026-02-28 18:44:13 +08:00
Carter Li 5087ea77b4 Presets: replaces outer single-line border with double-lines in examples/25 2026-02-28 17:48:36 +08:00
李通洲 b6b42e69c3 DisplayServer (Windows): reduce system dependencies 2026-02-28 10:37:03 +08:00
李通洲 000a688bb6 TerminalShell (Windows): improves logic of GUI program check 2026-02-28 10:06:13 +08:00
李通洲 3e25b213b1 WM (Windows): supports komorebi detection 2026-02-28 10:03:27 +08:00
李通洲 a69cf456ea Binary (Windows): removes imagehlp dep 2026-02-27 22:59:46 +08:00
李通洲 072b76cb12 WM: defaults detectPlugin to true 2026-02-27 18:26:53 +08:00
Carter Li ca8f54d331 CI: forwards envs & upgrades deps 2026-02-27 15:44:09 +08:00
李通洲 6423feaa94 Revert "OS (Windows): restores compatibility with WINE"
Seems WINE now has winbrand.dll

This reverts commit 554184ec72.
2026-02-27 15:41:55 +08:00
李通洲 92ada88ea7 WM (Windows): detect FancyWM & GlazeWM and their versions 2026-02-27 15:35:32 +08:00
李通洲 1789ef85d7 Processing (Windows): tidy 2026-02-27 15:04:43 +08:00
李通洲 42509f115c Common (Windows): always uses en_US when fetching exe version 2026-02-27 15:04:28 +08:00
李通洲 340a58bda1 Shell (Windows): ignore lldb-dap 2026-02-27 15:03:15 +08:00
李通洲 08dfd1321c Platform (macOS): use _NSGetExecutablePath to query current exe path
Unlike proc_pidpath which uses syscall
2026-02-26 15:12:53 +08:00
Carter Li 45d324a8c6 OpenGL: releases EGL context to avoid memleaks 2026-02-26 10:11:23 +08:00
Carter Li afa986efd4 Global (Linux): unrefs dbus replies to avoid memleaks 2026-02-26 10:11:12 +08:00
Carter Li bc6e8c7813 Global (Linux): unrefs dbus connection to avoid memleaks 2026-02-26 09:47:18 +08:00
Carter Li 627fee23d1 Chore: adds assertions 2026-02-26 09:00:24 +08:00
Carter Li 27b474f205 CPU: ensures tempSensor accepts string only
Fixes #2202
2026-02-26 08:55:53 +08:00
李通洲 418c7eecc7 Logo (Builtin): updates secureblue 2026-02-25 13:51:46 +08:00
李通洲 350250d640 Doc: updates json schema [ci skip] 2026-02-25 13:24:33 +08:00
Alxhr0 3cc21905af Editor: adds Zed version detection support (#2200) 2026-02-25 10:56:28 +08:00
underscorejoser c698eb8eb5 Logo (Builtin): fixes Fedora secureblue (#2201)
* fix: colored inside shackle

* Update ASCII art in secureblue.txt

* Fix formatting in secureblue.txt

---------

Co-authored-by: Carter Li <CarterLi@users.noreply.github.com>
2026-02-25 10:55:47 +08:00
Carter Li 463c24beb8 WM (Linux): for LXQt, only query wm settings when wm was not detected
Fixes #2199
2026-02-25 10:37:18 +08:00
李通洲 a5d1b96ecc Logo (Builtin): update ArcoLinux entries to reflect discontinuation and simplify name list 2026-02-24 13:16:58 +08:00
李通洲 63106ebdc1 Logo (Builtin): simplify name entries for Artix and its variants 2026-02-24 12:59:52 +08:00
李通洲 fd36783adb Logo (Builtin): removes redundant name entry for Exherbo 2026-02-24 12:49:02 +08:00
李通洲 3b5186da53 Logo (Builtin): rename ExodiaPredator to ExodiaOS and simplify name entries 2026-02-24 12:48:31 +08:00
李通洲 0140a46090 Logo (Builtin): corrects name casing for Deepin and removes redundant entry 2026-02-24 12:18:04 +08:00
李通洲 9c38e1255f Logo (Builtin): remove redundant names for Debian and DebianSmall 2026-02-24 12:17:56 +08:00
李通洲 663f9d69ff Logo (Builtin): corrects spelling of LinuxLight to LinuxLite 2026-02-24 12:05:52 +08:00
李通洲 d12e5ab801 Logo (Builtin): tidy 2026-02-24 11:56:25 +08:00
李通洲 1d770d85f6 Logo (Builtin): simplifies MassOS name entry 2026-02-24 11:56:06 +08:00
李通洲 5b00afe45d Logo (Builtin): simplifies MiracleLinux name entry 2026-02-24 11:55:41 +08:00
李通洲 5b276567ac Logo (Builtin): simplifies MX and MXSmall name entries 2026-02-24 11:55:22 +08:00
李通洲 56566916e3 Logo (Builtin): simplifies Nobara name entries 2026-02-24 11:54:48 +08:00
李通洲 2c0b4ca3a2 Logo (Builtin): simplifies Raspbian name entries 2026-02-24 11:45:29 +08:00
李通洲 54ffdd32dc Logo (Builtin): simplifies RedOS name entries 2026-02-24 11:45:14 +08:00
李通洲 64f24bdee9 Logo (Builtin): renames Refracted Devuan to Refracta
Ref: https://github.com/dylanaraps/neofetch/issues/2338
2026-02-24 11:41:21 +08:00
李通洲 4706f97bf0 Logo (Builtin): simplifies Rocky Linux 2026-02-24 11:38:01 +08:00
李通洲 0bdb198d52 Logo (Builtin): updates RengeOS entry to remove redundant name 2026-02-24 11:02:11 +08:00
s0s fc7710821e Common: fixes segmentation fault triggered by dereferencing value at negative index (#2198) 2026-02-24 11:00:25 +08:00
thealio e22a9990ec Logo (Builtin): Aritx logos improvements (#2157)
* Create artix4.txt

* Rename artix4.txt to artix2.txt

* Rename artix_small.txt to artix1_small.txt

* Rename artix2_small.txt to artix_small.txt

* Rename artix1_small.txt to artix2_small.txt
2026-02-24 10:59:47 +08:00
Crystal_ForceIX fc7f3a1650 Logo (Builtin): adds RengeOS Logo (#2170) 2026-02-24 10:52:39 +08:00
Carter Li 915af9546d Keyboard (Linux): supports more keyboard events 2026-02-22 22:48:17 +08:00
Carter Li 54a68640ef Packages (Linux): supports moss
Fixes #2191
2026-02-22 22:33:27 +08:00
李通洲 ec1af7c92b Platform (OpenBSD): improves executable path retrieval method
Fixes #2195
2026-02-22 18:14:36 +08:00
Carter Li a1e20270ad Platform: add current working directory (cwd) to FFPlatform structure and initialization 2026-02-21 00:29:02 +08:00
Chiron 4996986ce6 Logo (Builtin): updates Linux Mint ASCII Logo (#2186) 2026-02-19 02:56:56 +08:00
李通洲 e6de2d4fd9 Doc: update issue template [ci skip] 2026-02-19 00:50:43 +08:00
李通洲 5347e74a1b Logo (Builtin): adds linuxmint2
Ref: #2186
2026-02-18 23:58:39 +08:00
李通洲 0ea5639a22 Platform (Windows): prefers Nt* APIs 2026-02-18 20:13:33 +08:00
Carter Li aa3a916d5a Merge pull request #2183 from fastfetch-cli/dev
Release: v2.59.0
2026-02-14 00:15:11 +08:00
Carter Li 6e64b758bf Chore: silences a compiler warning 2026-02-13 23:37:40 +08:00
李通洲 49ef7fc51f Packages (Windows): code cleanups 2026-02-13 20:50:45 +08:00
李通洲 0e9a5acdbd Platform (Windows): adds init / destroy of field sid 2026-02-13 20:42:35 +08:00
李通洲 4f6e80e940 Release: v2.59.0 2026-02-13 19:39:33 +08:00
李通洲 df87cdfa6e Windows: defines NtCurrentProcess and uses it 2026-02-13 19:38:22 +08:00
李通洲 dee4b36a46 Bluetooth (Windows): use modern dlls 2026-02-13 19:25:06 +08:00
李通洲 d04a26337a Swap (Windows): removes unused code 2026-02-13 19:25:06 +08:00
Dark Knightz 2afe8d11d1 Common: fixes discarded-qualifiers warnings (#2181) 2026-02-13 18:05:07 +08:00
李通洲 f31d3b8b21 Doc: update changelog 2026-02-13 10:04:54 +08:00
李通洲 8a20af9917 Doc: update JSON schema 2026-02-13 10:02:28 +08:00
李通洲 fc5c2f6213 Title: adds {user-id} in custom format 2026-02-13 09:38:22 +08:00
Carter Li 6c314e148a Logo (Builtin): updates nixos_small too
Ref #2180
2026-02-13 09:09:05 +08:00
Konnor Klercke a86d0017a9 Logo (Builtin): adds 6-color support to nixos logo (#2180)
Co-authored-by: Konnor Klercke <kklercke@brite.com>
2026-02-13 08:58:48 +08:00
李通洲 4038ed2656 GPU: adds debug logs
Thanks Github Copilot
2026-02-12 18:56:42 +08:00
李通洲 0f4bb47c2f Global: fixes compiler warnings 2026-02-12 16:25:33 +08:00
李通洲 19f7c67ede Platform: adds pid field and use it 2026-02-12 14:29:49 +08:00
李通洲 ff7e81ed81 Memory (Windows): adds comments [ci skip] 2026-02-11 21:47:20 +08:00
李通洲 681a102a2b Processing (Windows): adds TODO [ci skip] 2026-02-11 21:47:09 +08:00
李通洲 ba27f4f2ef CI: adds Solaris 2026-02-11 15:50:58 +08:00
李通洲 cbe33a4860 CI (DragonFly): disables packaging .pkg file to fix a weird CI error 2026-02-11 15:50:58 +08:00
李通洲 5847e3f269 FFPlatform (Windows): fixes memory corruption 2026-02-11 10:03:29 +08:00
李通洲 b248dfa01c Platform: prefers system API to retreive user name 2026-02-10 23:42:57 +08:00
李通洲 357aa77e21 Media (Solaris): fixes another compiling error
Fixes #2173
2026-02-10 22:42:36 +08:00
李通洲 a44bde6f94 Platform: adds UID / SID detection 2026-02-10 20:33:28 +08:00
李通洲 5825bce57d Disk (Windows): replaces most Kernel32 calls to ntdll calls
* Improves accurancy of detection of drive create time
* Improves performance slightly
* Removes remove drive check, `hideFolders` should be used instead
2026-02-10 18:50:35 +08:00
李通洲 996cdd3155 Refactor: moves memrchr to global impl for potential future use 2026-02-10 18:16:58 +08:00
李通洲 61cdfcabf8 Windows: uses SHGetKnownFolderPath instead of the unreliable getenv 2026-02-10 17:32:34 +08:00
李通洲 359a77097c Terminal (Windows): uses NtQueryInformationProcess to retrieve information of conhost.exe 2026-02-10 17:32:34 +08:00
李通洲 1323d007d3 Global: uses alignas where applicable 2026-02-10 17:32:34 +08:00
李通洲 d2215f26ac Processing (Windows): uses NtQueryInformationProcess instead of its wrapper 2026-02-10 17:32:34 +08:00
李通洲 958e7dbd8f Bluetooth (Windows): simplifies code 2026-02-10 17:32:34 +08:00
Verdgil 5b10cde59f Global: Add support for Oracle Solaris (#2176)
* Fix Oracle Solaris zpool build

* Add memrchr fallback

* Change to include fron extern

* add new line

* remove ; in zpool.c

* Change wrapper to macro

Also change README.md

* Tweaks

---------

Co-authored-by: Carter Li <CarterLi@users.noreply.github.com>
2026-02-10 17:32:20 +08:00
李通洲 e8f2b9a1c7 Common: uses _Countof in ARRAY_SIZE definition when available 2026-02-02 11:08:26 +08:00
Carter Li 0686d1db4a GPU (Hurd): uses native API; removes libpciaccess dep 2026-01-30 20:34:47 +08:00
李通洲 7c284f9ee8 CMake (macOS): disable ENABLE_APPLE_MEMSIZE_USABLE by default 2026-01-29 13:11:48 +08:00
李通洲 dc04273bd7 Memory (macOS): updates algorithm in order not to use hw.memsize_usable which is available on newer macOS version
Adds a new CMake option to use usable memory size at the same time

Fixes #2171
2026-01-29 09:34:50 +08:00
李通洲 d698be6239 CPU (ARM): removes duplicated cases 2026-01-27 14:48:05 +08:00
李通洲 e44df27c96 Memory (macOS): adds debug info
Usage: `fastfetch -s memory:swap -l none --dynamic-interval 1000 --debug --no-buffer`
See https://github.com/fastfetch-cli/fastfetch/issues/2171#issuecomment-3803309478
2026-01-27 14:24:38 +08:00
李通洲 b365d4cca1 CPU (ARM): adds more ARM parts 2026-01-26 10:17:28 +08:00
李通洲 19d3c948ad DE (Android): clarifies that Google Pixel and other OSes also report ro.build.display.id 2026-01-26 10:17:28 +08:00
李通洲 4a581039d5 CI: fixes building 2026-01-24 09:17:13 +08:00
李通洲 79849fb2b5 CI: updates dependencies 2026-01-23 10:26:04 +08:00
李通洲 c116a5abdf Chore: uses elvis operators where applicatable 2026-01-22 15:36:42 +08:00
Carter Li 8f082730a8 Packaging: update debian stuff [ci skip] 2026-01-22 15:35:44 +08:00
277 changed files with 7979 additions and 3310 deletions
@@ -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"
+107 -74
View File
@@ -13,7 +13,7 @@ jobs:
steps:
- name: checkout repository
uses: actions/checkout@v5
uses: actions/checkout@v6
- name: Install codespell
shell: bash
@@ -32,13 +32,13 @@ jobs:
contents: read
steps:
- name: checkout repository
uses: actions/checkout@v5
uses: actions/checkout@v6
- name: uname -a
run: uname -a
- name: configure project
run: cmake -DSET_TWEAK=Off -DBUILD_TESTS=On -DCMAKE_INSTALL_PREFIX=/usr . -DENABLE_VULKAN=OFF -DENABLE_WAYLAND=OFF -DENABLE_XCB_RANDR=OFF -DENABLE_XCB=OFF -DENABLE_XRANDR=OFF -DENABLE_X11=OFF -DENABLE_DRM=OFF -DENABLE_DRM_AMDGPU=OFF -DENABLE_GIO=OFF -DENABLE_DCONF=OFF -DENABLE_DBUS=OFF -DENABLE_SQLITE3=OFF -DENABLE_RPM=OFF -DENABLE_IMAGEMAGICK7=OFF -DENABLE_IMAGEMAGICK6=OFF -DENABLE_CHAFA=OFF -DENABLE_ZLIB=OFF -DENABLE_EGL=OFF -DENABLE_GLX=OFF -DENABLE_OPENCL=OFF -DENABLE_FREETYPE=OFF -DENABLE_PULSE=OFF -DENABLE_DDCUTIL=OFF -DENABLE_ELF=OFF -DENABLE_DIRECTX_HEADERS=OFF -DENABLE_THREADS=OFF
run: cmake -DSET_TWEAK=Off -DBUILD_TESTS=On -DCMAKE_INSTALL_PREFIX=/usr . -DENABLE_VULKAN=OFF -DENABLE_WAYLAND=OFF -DENABLE_XCB_RANDR=OFF -DENABLE_XCB=OFF -DENABLE_XRANDR=OFF -DENABLE_X11=OFF -DENABLE_DRM=OFF -DENABLE_DRM_AMDGPU=OFF -DENABLE_GIO=OFF -DENABLE_DCONF=OFF -DENABLE_DBUS=OFF -DENABLE_SQLITE3=OFF -DENABLE_RPM=OFF -DENABLE_IMAGEMAGICK7=OFF -DENABLE_IMAGEMAGICK6=OFF -DENABLE_CHAFA=OFF -DENABLE_ZLIB=OFF -DENABLE_EGL=OFF -DENABLE_GLX=OFF -DENABLE_OPENCL=OFF -DENABLE_FREETYPE=OFF -DENABLE_PULSE=OFF -DENABLE_DDCUTIL=OFF -DENABLE_ELF=OFF -DENABLE_THREADS=OFF
- name: build project
run: cmake --build . --target package --verbose -j4
@@ -78,7 +78,7 @@ jobs:
ffversion: ${{ steps.ffversion.outputs.ffversion }}
steps:
- name: checkout repository
uses: actions/checkout@v5
uses: actions/checkout@v6
- name: uname -a
run: uname -a
@@ -93,28 +93,28 @@ 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 libvulkan-dev libwayland-dev libxrandr-dev libxcb-randr0-dev libdconf-dev libdbus-1-dev libmagickcore-dev libsqlite3-dev librpm-dev libegl-dev libglx-dev ocl-icd-opencl-dev libpulse-dev libdrm-dev libelf-dev libddcutil-dev rpm ninja-build
- name: install linuxbrew packages
run: |
/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'
uses: github/codeql-action/init@v3
uses: github/codeql-action/init@v4
with:
languages: c
- name: configure project
run: CC=gcc-13 CXX=g++-13 PKG_CONFIG_PATH=/home/linuxbrew/.linuxbrew/lib/pkgconfig:$PKG_CONFIG_PATH cmake -GNinja -DSET_TWEAK=Off -DBUILD_TESTS=On -DENABLE_EMBEDDED_PCIIDS=On -DENABLE_EMBEDDED_AMDGPUIDS=On -DCMAKE_INSTALL_PREFIX=/usr .
run: CC=gcc-13 PKG_CONFIG_PATH=/home/linuxbrew/.linuxbrew/lib/pkgconfig:$PKG_CONFIG_PATH cmake -GNinja -DSET_TWEAK=Off -DBUILD_TESTS=On -DENABLE_EMBEDDED_PCIIDS=On -DENABLE_EMBEDDED_AMDGPUIDS=On -DCMAKE_INSTALL_PREFIX=/usr .
- name: build project
run: cmake --build . --target package --verbose -j4
- name: perform CodeQL analysis
if: matrix.arch == 'amd64'
uses: github/codeql-action/analyze@v3
uses: github/codeql-action/analyze@v4
- name: list features
run: ./fastfetch --list-features
@@ -151,7 +151,7 @@ jobs:
cpack -V
- name: upload artifacts
uses: actions/upload-artifact@v4
uses: actions/upload-artifact@v7
with:
name: fastfetch-linux-${{ matrix.arch }}
path: ./fastfetch-*.*
@@ -164,7 +164,7 @@ jobs:
contents: read
steps:
- name: checkout repository
uses: actions/checkout@v5
uses: actions/checkout@v6
- name: uname -a
run: uname -a
@@ -190,7 +190,7 @@ jobs:
run: cmake --version
- name: configure project
run: CC=gcc-13 PKG_CONFIG_PATH=/home/linuxbrew/.linuxbrew/lib/pkgconfig:$PKG_CONFIG_PATH cmake -DCMAKE_C_FLAGS="-m32 -march=i686 -mtune=i686" -DCMAKE_SYSTEM_PROCESSOR_OVERRIDE=i686 -DCPACK_DEBIAN_PACKAGE_ARCHITECTURE=i386 -GNinja -DSET_TWEAK=Off -DBUILD_TESTS=On -DENABLE_EMBEDDED_PCIIDS=On -DENABLE_EMBEDDED_AMDGPUIDS=On -DCMAKE_INSTALL_PREFIX=/usr -DENABLE_DIRECTX_HEADERS=Off .
run: CC=gcc-13 PKG_CONFIG_PATH=/home/linuxbrew/.linuxbrew/lib/pkgconfig:$PKG_CONFIG_PATH cmake -DCMAKE_C_FLAGS="-m32 -march=i686 -mtune=i686" -DCMAKE_SYSTEM_PROCESSOR_OVERRIDE=i686 -DCPACK_DEBIAN_PACKAGE_ARCHITECTURE=i386 -GNinja -DSET_TWEAK=Off -DBUILD_TESTS=On -DENABLE_EMBEDDED_PCIIDS=On -DENABLE_EMBEDDED_AMDGPUIDS=On -DCMAKE_INSTALL_PREFIX=/usr .
- name: build project
run: cmake --build . --target package --verbose -j4
@@ -217,7 +217,7 @@ jobs:
run: ctest --output-on-failure
- name: upload artifacts
uses: actions/upload-artifact@v4
uses: actions/upload-artifact@v7
with:
name: fastfetch-linux-i686
path: ./fastfetch-*.*
@@ -230,24 +230,24 @@ jobs:
contents: read
steps:
- name: checkout repository
uses: actions/checkout@v5
uses: actions/checkout@v6
- name: run VM
uses: uraimo/run-on-arch-action@v3
id: runcmd
with:
arch: armv7
distro: ubuntu20.04
distro: ubuntu22.04
githubToken: ${{ github.token }}
run: |
uname -a
apt-get update && apt-get install -y software-properties-common ca-certificates gpg curl
add-apt-repository -y ppa:ubuntu-toolchain-r/test
curl -L https://apt.kitware.com/keys/kitware-archive-latest.asc | gpg --dearmor - | tee /usr/share/keyrings/kitware-archive-keyring.gpg >/dev/null
echo 'deb [signed-by=/usr/share/keyrings/kitware-archive-keyring.gpg] https://apt.kitware.com/ubuntu/ focal main' | tee /etc/apt/sources.list.d/kitware.list >/dev/null
echo 'deb [signed-by=/usr/share/keyrings/kitware-archive-keyring.gpg] https://apt.kitware.com/ubuntu/ jammy main' | tee /etc/apt/sources.list.d/kitware.list >/dev/null
echo -e 'Acquire::https::Verify-Peer "false";\nAcquire::https::Verify-Host "false";' >> /etc/apt/apt.conf.d/99ignore-certificates
apt-get update && apt-get install -y cmake make gcc-13 libvulkan-dev libwayland-dev libxrandr-dev libxcb-randr0-dev libdconf-dev libdbus-1-dev libmagickcore-dev libsqlite3-dev librpm-dev libegl-dev libglx-dev ocl-icd-opencl-dev libpulse-dev libdrm-dev libelf-dev rpm
CC=gcc-13 cmake -DSET_TWEAK=Off -DBUILD_TESTS=On -DENABLE_DIRECTX_HEADERS=Off -DCMAKE_INSTALL_PREFIX=/usr .
CC=gcc-13 cmake -DSET_TWEAK=Off -DBUILD_TESTS=On -DCMAKE_INSTALL_PREFIX=/usr .
cmake --build . --target package --verbose -j4
./fastfetch --list-features
time ./fastfetch -c presets/ci.jsonc --stat false
@@ -257,7 +257,7 @@ jobs:
ctest --output-on-failure
- name: upload artifacts
uses: actions/upload-artifact@v4
uses: actions/upload-artifact@v7
with:
name: fastfetch-linux-armv7l
path: ./fastfetch-*.*
@@ -270,7 +270,7 @@ jobs:
contents: read
steps:
- name: checkout repository
uses: actions/checkout@v5
uses: actions/checkout@v6
- name: run VM
uses: uraimo/run-on-arch-action@v3
@@ -283,7 +283,7 @@ jobs:
uname -a
apt-get update && apt-get install -y wget
apt-get install -y cmake make gcc libvulkan-dev libwayland-dev libxrandr-dev libxcb-randr0-dev libdconf-dev libdbus-1-dev libmagickcore-dev libsqlite3-dev librpm-dev libegl-dev libglx-dev ocl-icd-opencl-dev libpulse-dev libdrm-dev libelf-dev rpm
cmake -DSET_TWEAK=Off -DBUILD_TESTS=On -DENABLE_DIRECTX_HEADERS=Off -DCMAKE_INSTALL_PREFIX=/usr .
cmake -DSET_TWEAK=Off -DBUILD_TESTS=On -DCMAKE_INSTALL_PREFIX=/usr .
cmake --build . --target package --verbose -j4
./fastfetch --list-features
time ./fastfetch -c presets/ci.jsonc --stat false
@@ -293,7 +293,7 @@ jobs:
ctest --output-on-failure
- name: upload artifacts
uses: actions/upload-artifact@v4
uses: actions/upload-artifact@v7
with:
name: fastfetch-linux-armv6l
path: ./fastfetch-*.*
@@ -312,7 +312,7 @@ jobs:
- arch: s390x
steps:
- name: checkout repository
uses: actions/checkout@v5
uses: actions/checkout@v6
- name: run VM
uses: uraimo/run-on-arch-action@v3
@@ -326,7 +326,7 @@ jobs:
apt-get update && apt-get install -y software-properties-common
add-apt-repository -y ppa:ubuntu-toolchain-r/test
apt-get update && apt-get install -y cmake make gcc-13 libvulkan-dev libwayland-dev libxrandr-dev libxcb-randr0-dev libdconf-dev libdbus-1-dev libmagickcore-dev libsqlite3-dev librpm-dev libegl-dev libglx-dev ocl-icd-opencl-dev libpulse-dev libdrm-dev libchafa-dev libelf-dev rpm
CC=gcc-13 cmake -DSET_TWEAK=Off -DBUILD_TESTS=On -DENABLE_DIRECTX_HEADERS=Off -DCMAKE_INSTALL_PREFIX=/usr .
CC=gcc-13 cmake -DSET_TWEAK=Off -DBUILD_TESTS=On -DCMAKE_INSTALL_PREFIX=/usr .
cmake --build . --target package --verbose -j4
./fastfetch --list-features
time ./fastfetch -c presets/ci.jsonc --stat false
@@ -336,7 +336,7 @@ jobs:
ctest --output-on-failure
- name: upload artifacts
uses: actions/upload-artifact@v4
uses: actions/upload-artifact@v7
with:
name: fastfetch-linux-${{ matrix.arch }}
path: ./fastfetch-*.*
@@ -345,7 +345,7 @@ jobs:
name: Musl-amd64
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v5
- uses: actions/checkout@v6
- name: setup alpine linux
uses: jirutka/setup-alpine@master
@@ -374,7 +374,7 @@ jobs:
shell: alpine.sh {0}
- name: upload artifacts
uses: actions/upload-artifact@v4
uses: actions/upload-artifact@v7
with:
name: fastfetch-musl-amd64
path: ./fastfetch-*.*
@@ -394,7 +394,7 @@ jobs:
runs-on: macos-latest
steps:
- name: checkout repository
uses: actions/checkout@v5
uses: actions/checkout@v6
- name: uname -a
run: uname -a
@@ -413,10 +413,10 @@ jobs:
run: ./fastfetch --list-features
- name: run fastfetch
run: time ./fastfetch -c presets/ci.jsonc --stat false
run: time ./fastfetch -c presets/ci.jsonc --structure-disabled vulkan --stat false
- name: run fastfetch --format json
run: time ./fastfetch -c presets/ci.jsonc --format json
run: time ./fastfetch -c presets/ci.jsonc --structure-disabled vulkan --format json
- name: run flashfetch
run: time ./flashfetch
@@ -428,42 +428,80 @@ jobs:
run: ctest --output-on-failure
- name: upload artifacts
uses: actions/upload-artifact@v4
uses: actions/upload-artifact@v7
with:
name: fastfetch-macos-${{ matrix.arch }}
path: ./fastfetch-*.*
sunos-amd64:
omnios-amd64:
runs-on: ubuntu-latest
name: SunOS-amd64
name: OmniOS-amd64
steps:
- name: checkout repository
uses: actions/checkout@v5
uses: actions/checkout@v6
- name: run VM
uses: vmactions/omnios-vm@v1
with:
usesh: true
envs: 'CMAKE_BUILD_TYPE'
prepare: |
uname -a
pkg update --accept
pkg install gcc14 cmake git pkg-config glib2 dbus sqlite-3 imagemagick
pkg install gcc14 cmake git pkg-config glib2 dbus sqlite-3 imagemagick ninja
run: |
cmake -DSET_TWEAK=Off -DBUILD_TESTS=On .
cmake --build . --target package --verbose -j4
cmake -DSET_TWEAK=Off -DBUILD_TESTS=On -GNinja .
cmake --build . --verbose -j4
./fastfetch --list-features
time ./fastfetch -c presets/ci.jsonc --stat false
time ./fastfetch -c presets/ci.jsonc --format json
time ./flashfetch
ldd fastfetch
ctest --output-on-failure
cpack
echo 'set(CPACK_PACKAGE_FILE_NAME "fastfetch-omnios-amd64")' >> CPackConfig.cmake
cpack -V
- name: upload artifacts
uses: actions/upload-artifact@v4
uses: actions/upload-artifact@v7
with:
name: fastfetch-sunos-amd64
name: fastfetch-omnios-amd64
path: ./fastfetch-*.*
solaris-amd64:
runs-on: ubuntu-latest
name: Solaris-amd64
steps:
- name: checkout repository
uses: actions/checkout@v6
- name: run VM
uses: vmactions/solaris-vm@v1
with:
usesh: true
envs: 'CMAKE_BUILD_TYPE'
release: "11.4-gcc-14"
prepare: |
uname -a
pkg install cmake git pkg-config glib2 dbus sqlite-3 imagemagick ninja dconf mesa
run: |
export PKG_CONFIG_PATH=/usr/lib/64/pkgconfig
cmake -DSET_TWEAK=Off -DBUILD_TESTS=On -GNinja .
cmake --build . --verbose -j4
./fastfetch --list-features
time ./fastfetch -c presets/ci.jsonc --stat false
time ./fastfetch -c presets/ci.jsonc --format json
time ./flashfetch
ldd fastfetch
ctest --output-on-failure
echo 'set(CPACK_PACKAGE_FILE_NAME "fastfetch-solaris-amd64")' >> CPackConfig.cmake
cpack -V
- name: upload artifacts
uses: actions/upload-artifact@v7
with:
name: fastfetch-solaris-amd64
path: ./fastfetch-*.*
freebsd-amd64:
@@ -474,7 +512,7 @@ jobs:
contents: read
steps:
- name: checkout repository
uses: actions/checkout@v5
uses: actions/checkout@v6
- name: run VM
uses: cross-platform-actions/action@master
@@ -483,7 +521,8 @@ jobs:
architecture: x86-64
cpu_count: 4
shell: bash
version: '14.3'
version: '15.0'
environment_variables: 'CMAKE_BUILD_TYPE'
run: |
uname -a
sudo pkg update
@@ -498,7 +537,7 @@ jobs:
ctest --output-on-failure
- name: upload artifacts
uses: actions/upload-artifact@v4
uses: actions/upload-artifact@v7
with:
name: fastfetch-freebsd-amd64
path: ./fastfetch-*.*
@@ -511,7 +550,7 @@ jobs:
contents: read
steps:
- name: checkout repository
uses: actions/checkout@v5
uses: actions/checkout@v6
- name: run VM
uses: cross-platform-actions/action@master
@@ -521,6 +560,7 @@ jobs:
cpu_count: 4
shell: bash
version: '7.8'
environment_variables: 'CMAKE_BUILD_TYPE'
run: |
uname -a
sudo pkg_add -u
@@ -535,7 +575,7 @@ jobs:
ctest --output-on-failure
- name: upload artifacts
uses: actions/upload-artifact@v4
uses: actions/upload-artifact@v7
with:
name: fastfetch-openbsd-amd64
path: ./fastfetch-*.*
@@ -548,7 +588,7 @@ jobs:
contents: read
steps:
- name: checkout repository
uses: actions/checkout@v5
uses: actions/checkout@v6
- name: run VM
uses: cross-platform-actions/action@master
@@ -558,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
@@ -571,7 +612,7 @@ jobs:
ctest --output-on-failure
- name: upload artifacts
uses: actions/upload-artifact@v4
uses: actions/upload-artifact@v7
with:
name: fastfetch-netbsd-amd64
path: ./fastfetch-*.*
@@ -584,12 +625,13 @@ jobs:
contents: read
steps:
- name: checkout repository
uses: actions/checkout@v5
uses: actions/checkout@v6
- name: run VM
uses: vmactions/dragonflybsd-vm@v1
with:
usesh: yes
envs: 'CMAKE_BUILD_TYPE'
prepare: |
uname -a
pkg update
@@ -606,7 +648,7 @@ jobs:
ctest --output-on-failure
- name: upload artifacts
uses: actions/upload-artifact@v4
uses: actions/upload-artifact@v7
with:
name: fastfetch-dragonfly-amd64
path: ./fastfetch-*.*
@@ -619,7 +661,7 @@ jobs:
contents: read
steps:
- name: checkout repository
uses: actions/checkout@v5
uses: actions/checkout@v6
- name: run VM
uses: cross-platform-actions/action@master
@@ -629,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
@@ -641,13 +684,13 @@ jobs:
ctest --output-on-failure
- name: upload artifacts
uses: actions/upload-artifact@v4
uses: actions/upload-artifact@v7
with:
name: fastfetch-haiku-amd64
path: ./fastfetch-*.*
windows-hosts:
name: Windows-${{ matrix.arch }}${{ matrix.win7-compat-postfix }}
name: Windows-${{ matrix.arch }}
runs-on: ${{ matrix.runs-on }}
permissions:
security-events: write
@@ -660,41 +703,30 @@ jobs:
msystem: CLANG64
msystem-lower: clang64
msys-arch: x86_64
win7-compat-flag: OFF
win7-compat-postfix: ""
- arch: amd64
runs-on: windows-latest
msystem: CLANG64
msystem-lower: clang64
msys-arch: x86_64
win7-compat-flag: ON
win7-compat-postfix: "-win7"
- arch: aarch64
runs-on: windows-11-arm
msystem: CLANGARM64
msystem-lower: clangarm64
msys-arch: aarch64
win7-compat-flag: OFF
win7-compat-postfix: ""
defaults:
run:
shell: msys2 {0}
steps:
- name: checkout repository
uses: actions/checkout@v5
uses: actions/checkout@v6
- name: setup-msys2
uses: msys2/setup-msys2@v2
with:
msystem: ${{ matrix.msystem }}
update: true
install: git mingw-w64-clang-${{ matrix.msys-arch }}-7zip mingw-w64-clang-${{ matrix.msys-arch }}-cmake mingw-w64-clang-${{ matrix.msys-arch }}-clang mingw-w64-clang-${{ matrix.msys-arch }}-vulkan-loader mingw-w64-clang-${{ matrix.msys-arch }}-vulkan-headers mingw-w64-clang-${{ matrix.msys-arch }}-opencl-icd mingw-w64-clang-${{ matrix.msys-arch }}-opencl-headers mingw-w64-clang-${{ matrix.msys-arch }}-cppwinrt mingw-w64-clang-${{ matrix.msys-arch }}-imagemagick mingw-w64-clang-${{ matrix.msys-arch }}-chafa
install: git mingw-w64-clang-${{ matrix.msys-arch }}-7zip mingw-w64-clang-${{ matrix.msys-arch }}-cmake mingw-w64-clang-${{ matrix.msys-arch }}-clang mingw-w64-clang-${{ matrix.msys-arch }}-vulkan-loader mingw-w64-clang-${{ matrix.msys-arch }}-vulkan-headers mingw-w64-clang-${{ matrix.msys-arch }}-opencl-icd mingw-w64-clang-${{ matrix.msys-arch }}-opencl-headers mingw-w64-clang-${{ matrix.msys-arch }}-cppwinrt mingw-w64-clang-${{ matrix.msys-arch }}-imagemagick mingw-w64-clang-${{ matrix.msys-arch }}-chafa mingw-w64-clang-${{ matrix.msys-arch }}-directx-headers
- name: print msys version
run: uname -a
- name: configure project
run: env PKG_CONFIG_PATH=/${{ matrix.msystem-lower }}/lib/pkgconfig/:$PKG_CONFIG_PATH cmake -DSET_TWEAK=Off -DBUILD_TESTS=On -DENABLE_WIN7_COMPAT=${{ matrix.win7-compat-flag }} .
run: env PKG_CONFIG_PATH=/${{ matrix.msystem-lower }}/lib/pkgconfig/:$PKG_CONFIG_PATH cmake -DSET_TWEAK=Off -DBUILD_TESTS=On .
- name: build project
run: cmake --build . --verbose -j4
@@ -723,9 +755,9 @@ jobs:
- if: github.event_name == 'push' && github.repository == 'fastfetch-cli/fastfetch'
id: upload-unsigned-artifact
name: upload artifacts for signing
uses: actions/upload-artifact@v4
uses: actions/upload-artifact@v7
with:
name: fastfetch-windows-${{ matrix.arch }}${{ matrix.win7-compat-postfix }}
name: fastfetch-windows-${{ matrix.arch }}
path: |
*.dll
fastfetch.exe
@@ -744,16 +776,16 @@ jobs:
output-artifact-directory: '.'
- name: create zip archive
run: 7z a -tzip -mx9 -bd -y fastfetch-windows-${{ matrix.arch }}${{ matrix.win7-compat-postfix }}.zip LICENSE *.dll fastfetch.exe flashfetch.exe presets
run: 7z a -tzip -mx9 -bd -y fastfetch-windows-${{ matrix.arch }}.zip LICENSE *.dll fastfetch.exe flashfetch.exe presets
- name: create 7z archive
run: 7z a -t7z -mx9 -bd -y fastfetch-windows-${{ matrix.arch }}${{ matrix.win7-compat-postfix }}.7z LICENSE *.dll fastfetch.exe flashfetch.exe presets
run: 7z a -t7z -mx9 -bd -y fastfetch-windows-${{ matrix.arch }}.7z LICENSE *.dll fastfetch.exe flashfetch.exe presets
- name: upload true artifacts
uses: actions/upload-artifact@v4
uses: actions/upload-artifact@v7
with:
name: fastfetch-windows-${{ matrix.arch }}${{ matrix.win7-compat-postfix }}
path: ./fastfetch-windows-${{ matrix.arch }}${{ matrix.win7-compat-postfix }}.*
name: fastfetch-windows-${{ matrix.arch }}
path: ./fastfetch-windows-${{ matrix.arch }}.*
overwrite: true
release:
@@ -772,7 +804,8 @@ jobs:
- openbsd-amd64
- netbsd-amd64
- dragonfly-amd64
- sunos-amd64
- solaris-amd64
- omnios-amd64
- haiku-amd64
- windows-hosts
permissions:
@@ -786,7 +819,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
+87 -2
View File
@@ -1,3 +1,88 @@
# 2.61.0
Changes:
* Support for Windows 7 and 8 has been removed.
* Windows 8.1 is now the oldest version supported by fastfetch.
* The GPU module on WSL no longer relies on `DXCore`.
* The `directx-headers` dependency is no longer required.
* Fastfetch on Linux is now pure C; a C++ compiler is no longer required.
* GPU type detection is now slightly less accurate, but detection speed should be slightly faster.
* The GPU module on Windows now uses `DXCore` for more accurate GPU type detection (requires Windows 10 or later).
* This feature is built only when `DXCore` headers are available, which requires installing `mingw-w64-<msystem>-x86_64-directx-headers` on MSYS2.
Features:
* Adds a `brightness` option for color display configuration (#2238, Colors)
* Adds support for detecting Bluetooth keyboards on Linux (#2220, Keyboard)
* Adds support for detecting GlazeWM (WM, macOS)
* Adds a `showEmptySlots` option to display empty memory slots on Linux (#2222, PhysicalMemory)
* Adds marketing product name detection on Asahi Linux (Host, Linux)
* Adds support for new M5 Mac models (Host, macOS)
* Improves SMBIOS robustness by validating malformed data and improving error handling
* Improves reliability when terminating child processes (Processing, Windows)
* Improves Intel Mac support by querying SMBIOS data directly (Global, macOS)
* Includes numerous internal cleanups and optimizations
Bugfixes:
* Fixes missing memory devices on some machines (PhysicalMemory)
* Fixes CPUCache deduplication for shared caches (#2228, CPUCache, Linux)
* Fixes WM version reporting for niri (#2218, WM, Linux)
* Fixes SSID decoding issues from `iw` output (Wifi, Linux)
* Fixes the CMD code page being changed after running fastfetch on Windows (#2245, Windows)
# 2.60.0
Changes:
* 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:
* Fastfetch no longer relies on the unreliable environment variables `$USER` or `%USERPROFILE%` to determine the current username (Title)
* People who set `$USER` to customize the Fastfetch title should use `{ "type": "title", "format": "your-custom-user-name" }` to achieve the same result.
* Fastfetch no longer tries to probe inaccessible remote disk drives on Windows (Disk, Windows)
* People who have remote drives may use `{ "type": "disk", "hideFolders": "X:\\" }` to ignore problematic ones.
* This change removes some ugly hacks from the codebase and matches the behavior on `*nix`.
Features:
* Adds Oracle Solaris support (#2176, SunOS)
* Adds UID / SID detection (Title)
* In custom format: `{user-id}`
* Switches to native GPU detection on GNU/Hurd and removes the `libpciaccess` dependency (GPU, Hurd)
* Improves memory size detection on macOS (Memory, macOS)
* Avoids relying on `hw.memsize_usable` by default, which may not be available on older macOS versions
* Improves Windows disk detection accuracy and performance (Disk, Windows)
* Adds more ARM CPU parts and removes duplicated cases (CPU, ARM)
Logos:
* Adds 6-color support to the NixOS logo (including the small variant) (#2180)
# 2.58.0
An early release to fix compatibility issues with KDE Plasma 6.6.
@@ -13,7 +98,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)
@@ -40,7 +125,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)
+77 -43
View File
@@ -1,7 +1,7 @@
cmake_minimum_required(VERSION 3.12.0) # target_link_libraries with OBJECT libs & project homepage url
project(fastfetch
VERSION 2.58.0
VERSION 2.61.0
LANGUAGES C
DESCRIPTION "Fast neofetch-like system information tool"
HOMEPAGE_URL "https://github.com/fastfetch-cli/fastfetch"
@@ -89,10 +89,8 @@ cmake_dependent_option(ENABLE_OPENCL "Enable opencl" ON "LINUX OR FreeBSD OR Ope
cmake_dependent_option(ENABLE_FREETYPE "Enable freetype" ON "ANDROID" OFF)
cmake_dependent_option(ENABLE_PULSE "Enable pulse" ON "LINUX OR ANDROID OR GNU" OFF)
cmake_dependent_option(ENABLE_DDCUTIL "Enable ddcutil" ON "LINUX" OFF)
cmake_dependent_option(ENABLE_DIRECTX_HEADERS "Enable DirectX headers for WSL" ON "LINUX" OFF)
cmake_dependent_option(ENABLE_ELF "Enable libelf" ON "LINUX OR ANDROID OR DragonFly OR Haiku OR GNU" OFF)
cmake_dependent_option(ENABLE_THREADS "Enable multithreading" ON "Threads_FOUND" OFF)
cmake_dependent_option(ENABLE_PCIACCESS "Enable libpciaccess" ON "GNU" OFF)
option(ENABLE_ZLIB "Enable zlib" ON)
option(ENABLE_SYSTEM_YYJSON "Use system provided (instead of fastfetch embedded) yyjson library" OFF)
@@ -108,7 +106,10 @@ 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_WIN81_COMPAT "Enable legacy Windows compatibility (Windows 8.1 and later; Windows 7 unsupported)" ON)
endif()
if(APPLE)
option(ENABLE_APPLE_MEMSIZE_USABLE "Use usable memory size as total memory size in Memory module, to match other systems" OFF)
endif()
set(BINARY_LINK_TYPE_OPTIONS dlopen dynamic static)
@@ -157,7 +158,7 @@ set(WARNING_FLAGS "-Wall -Wextra -Wconversion -Werror=uninitialized -Werror=retu
set(CMAKE_C_STANDARD 11)
set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} ${WARNING_FLAGS} -Werror=incompatible-pointer-types -Werror=implicit-function-declaration -Werror=int-conversion")
if(WIN32 OR Haiku OR ENABLE_DIRECTX_HEADERS)
if(WIN32 OR Haiku)
enable_language(CXX)
set(CMAKE_CXX_STANDARD 17)
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} ${WARNING_FLAGS}")
@@ -166,8 +167,8 @@ endif()
if(WIN32)
set(CMAKE_EXE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS} -Wl,--tsaware -Wl,--build-id")
if(ENABLE_WIN7_COMPAT)
set(CMAKE_EXE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS} -Wl,--subsystem,console,--major-os-version,6,--minor-os-version,1")
if(ENABLE_WIN81_COMPAT)
set(CMAKE_EXE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS} -Wl,--subsystem,console,--major-os-version,6,--minor-os-version,3")
else()
set(CMAKE_EXE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS} -Wl,--subsystem,console,--major-os-version,10,--minor-os-version,0")
endif()
@@ -303,7 +304,8 @@ if(ENABLE_EMBEDDED_PCIIDS AND NOT EXISTS "${PROJECT_BINARY_DIR}/fastfetch_pciids
RESULT_VARIABLE PYTHON_PCIIDS_RETCODE)
if(NOT PYTHON_PCIIDS_RETCODE EQUAL 0)
file(REMOVE "${PROJECT_BINARY_DIR}/fastfetch_pciids.c.inc")
message(FATAL_ERROR "Failed to generate 'fastfetch_pciids.c.inc'")
message(WARNING "Failed to generate 'fastfetch_pciids.c.inc'")
set(ENABLE_EMBEDDED_PCIIDS OFF)
endif()
else()
message(WARNING "Python3 is not found, 'fastfetch_pciids.c.inc' will not be generated")
@@ -323,7 +325,8 @@ if(ENABLE_EMBEDDED_AMDGPUIDS AND NOT EXISTS "${PROJECT_BINARY_DIR}/fastfetch_amd
RESULT_VARIABLE PYTHON_AMDGPUIDS_RETCODE)
if(NOT PYTHON_AMDGPUIDS_RETCODE EQUAL 0)
file(REMOVE "${PROJECT_BINARY_DIR}/fastfetch_amdgpuids.c.inc")
message(FATAL_ERROR "Failed to generate 'fastfetch_amdgpuids.c.inc'")
message(WARNING "Failed to generate 'fastfetch_amdgpuids.c.inc'")
set(ENABLE_EMBEDDED_AMDGPUIDS OFF)
endif()
else()
message(WARNING "Python3 is not found, 'fastfetch_amdgpuids.c.inc' will not be generated")
@@ -396,7 +399,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
@@ -412,6 +414,7 @@ set(LIBFASTFETCH_SRC
src/detection/editor/editor.c
src/detection/font/font.c
src/detection/gpu/gpu.c
src/detection/host/host_mac.c
src/detection/media/media.c
src/detection/netio/netio.c
src/detection/opencl/opencl.c
@@ -521,6 +524,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
@@ -557,9 +561,9 @@ if(LINUX)
src/detection/gpu/gpu_linux.c
src/detection/gpu/gpu_drm.c
src/detection/gpu/gpu_pci.c
src/detection/gpu/gpu_wsl.c
src/detection/gtk_qt/gtk.c
src/detection/host/host_linux.c
src/detection/host/host_mac.c
src/detection/icons/icons_linux.c
src/detection/initsystem/initsystem_linux.c
src/detection/keyboard/keyboard_linux.c
@@ -605,6 +609,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
@@ -679,6 +684,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
@@ -716,7 +722,6 @@ elseif(FreeBSD)
src/detection/gpu/gpu_pci.c
src/detection/gtk_qt/gtk.c
src/detection/host/host_bsd.c
src/detection/host/host_mac.c
src/detection/lm/lm_linux.c
src/detection/icons/icons_linux.c
src/detection/initsystem/initsystem_linux.c
@@ -772,6 +777,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
@@ -855,6 +861,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
@@ -936,18 +943,16 @@ 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
src/detection/battery/battery_apple.c
src/detection/bios/bios_apple.c
src/detection/bluetooth/bluetooth_apple.m
src/detection/bluetoothradio/bluetoothradio_apple.m
src/detection/board/board_apple.c
src/detection/bootmgr/bootmgr_apple.c
src/detection/brightness/brightness_apple.c
src/detection/btrfs/btrfs_nosupport.c
src/detection/chassis/chassis_apple.c
src/detection/cpu/cpu_apple.c
src/detection/cpucache/cpucache_apple.c
src/detection/cpuusage/cpuusage_apple.c
@@ -955,14 +960,11 @@ elseif(APPLE)
src/detection/disk/disk_bsd.c
src/detection/dns/dns_apple.c
src/detection/physicaldisk/physicaldisk_apple.c
src/detection/physicalmemory/physicalmemory_apple.m
src/detection/diskio/diskio_apple.c
src/detection/displayserver/displayserver_apple.c
src/detection/font/font_apple.m
src/detection/gpu/gpu_apple.c
src/detection/gpu/gpu_apple.m
src/detection/host/host_apple.c
src/detection/host/host_mac.c
src/detection/icons/icons_nosupport.c
src/detection/initsystem/initsystem_linux.c
src/detection/keyboard/keyboard_apple.c
@@ -998,6 +1000,24 @@ elseif(APPLE)
src/detection/wmtheme/wmtheme_apple.m
src/detection/camera/camera_apple.m
)
# CMAKE_SYSTEM_PROCESSOR has been normalized before
if(CMAKE_SYSTEM_PROCESSOR STREQUAL "amd64")
list(APPEND LIBFASTFETCH_SRC
src/detection/bios/bios_windows.c
src/detection/board/board_windows.c
src/detection/chassis/chassis_windows.c
src/detection/host/host_windows.c
src/detection/physicalmemory/physicalmemory_linux.c
)
else()
list(APPEND LIBFASTFETCH_SRC
src/detection/bios/bios_apple.c
src/detection/board/board_apple.c
src/detection/chassis/chassis_apple.c
src/detection/host/host_apple.c
src/detection/physicalmemory/physicalmemory_apple.m
)
endif()
elseif(WIN32)
list(APPEND LIBFASTFETCH_SRC
src/common/impl/io_windows.c
@@ -1007,11 +1027,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
@@ -1034,7 +1056,7 @@ elseif(WIN32)
src/detection/dns/dns_windows.c
src/detection/font/font_windows.c
src/detection/gpu/gpu_windows.c
src/detection/host/host_mac.c
src/detection/gpu/gpu_windows.cpp
src/detection/host/host_windows.c
src/detection/icons/icons_windows.c
src/detection/initsystem/initsystem_nosupport.c
@@ -1051,7 +1073,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
@@ -1066,7 +1088,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
@@ -1080,6 +1102,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
@@ -1161,6 +1184,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
@@ -1230,6 +1254,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
@@ -1259,7 +1284,7 @@ elseif(GNU)
src/detection/displayserver/linux/xcb.c
src/detection/displayserver/linux/xlib.c
src/detection/font/font_linux.c
src/detection/gpu/gpu_general.c
src/detection/gpu/gpu_gnu.c
src/detection/gpu/gpu_pci.c
src/detection/gtk_qt/gtk.c
src/detection/host/host_nosupport.c
@@ -1301,11 +1326,6 @@ elseif(GNU)
)
endif()
if(ENABLE_DIRECTX_HEADERS)
message(STATUS "Enabling DirectX headers for WSL")
list(APPEND LIBFASTFETCH_SRC src/detection/gpu/gpu_wsl.cpp)
endif()
# Proprietary GPU driver APIs
if(LINUX OR FreeBSD OR WIN32)
list(APPEND LIBFASTFETCH_SRC src/detection/gpu/gpu_nvidia.c)
@@ -1323,6 +1343,10 @@ endif()
if(NOT WIN32)
check_function_exists(pipe2 HAVE_PIPE2)
endif()
check_function_exists(memrchr HAVE_MEMRCHR)
if(NOT HAVE_MEMRCHR)
list(APPEND LIBFASTFETCH_SRC src/common/impl/memrchr.c)
endif()
if(ENABLE_SYSTEM_YYJSON)
find_package(yyjson)
@@ -1341,6 +1365,15 @@ add_library(libfastfetch OBJECT
${LIBFASTFETCH_SRC}
)
include(CheckCSourceCompiles)
check_c_source_compiles("int main(void){int arr[1];return _Countof(arr);}" COMPILER_SUPPORTS_COUNT_OF)
if(COMPILER_SUPPORTS_COUNT_OF)
message(STATUS "_Countof is supported by the compiler")
target_compile_definitions(libfastfetch PUBLIC FF_SUPPORTS_COUNT_OF=1)
else()
message(STATUS "_Countof is NOT supported by the compiler")
endif()
if(yyjson_FOUND)
target_compile_definitions(libfastfetch PUBLIC FF_USE_SYSTEM_YYJSON=1)
target_link_libraries(libfastfetch PUBLIC yyjson::yyjson)
@@ -1424,16 +1457,24 @@ elseif(GNU)
target_compile_definitions(libfastfetch PUBLIC _GNU_SOURCE PATH_MAX=4096 O_PATH=0)
endif()
if(APPLE)
if(ENABLE_APPLE_MEMSIZE_USABLE)
target_compile_definitions(libfastfetch PUBLIC FF_APPLE_MEMSIZE_USABLE=1)
else()
target_compile_definitions(libfastfetch PUBLIC FF_APPLE_MEMSIZE_USABLE=0)
endif()
endif()
if(WIN32)
check_function_exists(_msize HAVE_MSVC_MSIZE)
if(HAVE_MSVC_MSIZE)
target_compile_definitions(libfastfetch PUBLIC FF_HAVE_MSVC_MSIZE=1)
endif()
if(ENABLE_WIN7_COMPAT)
target_compile_definitions(libfastfetch PUBLIC FF_WIN7_COMPAT=1)
if(ENABLE_WIN81_COMPAT)
target_compile_definitions(libfastfetch PUBLIC FF_WIN81_COMPAT=1)
else()
target_compile_definitions(libfastfetch PUBLIC FF_WIN7_COMPAT=0)
target_compile_definitions(libfastfetch PUBLIC FF_WIN81_COMPAT=0)
endif()
else()
check_function_exists(malloc_usable_size HAVE_MALLOC_USABLE_SIZE)
@@ -1626,14 +1667,6 @@ ff_lib_enable(ELF
"libelf"
"libelf"
)
ff_lib_enable(DIRECTX_HEADERS
"DirectX-Headers"
"DirectX-Headers"
)
ff_lib_enable(PCIACCESS
"pciaccess"
"pciaccess"
)
if(ENABLE_THREADS)
target_compile_definitions(libfastfetch PRIVATE FF_HAVE_THREADS=1)
@@ -1657,6 +1690,9 @@ if(ENABLE_LIBZFS)
)
endif()
endif()
if(NOT HAVE_MEMRCHR)
target_compile_definitions(libfastfetch PRIVATE FF_HAVE_CUSTOM_MEMRCHR=1)
endif()
if(LINUX)
target_link_libraries(libfastfetch
@@ -1693,7 +1729,6 @@ elseif(APPLE)
)
elseif(WIN32)
target_link_libraries(libfastfetch
PRIVATE "dwmapi"
PRIVATE "gdi32"
PRIVATE "iphlpapi"
PRIVATE "ole32"
@@ -1703,12 +1738,11 @@ elseif(WIN32)
PRIVATE "version"
PRIVATE "hid"
PRIVATE "wtsapi32"
PRIVATE "imagehlp"
PRIVATE "cfgmgr32"
PRIVATE "propsys"
PRIVATE "winbrand"
PRIVATE "secur32"
)
if(NOT ENABLE_WIN7_COMPAT)
if(NOT ENABLE_WIN81_COMPAT)
target_link_libraries(libfastfetch
PRIVATE "mincore"
)
@@ -2089,7 +2123,7 @@ if(LINUX)
set(CPACK_RPM_PACKAGE_LICENSE "MIT")
endif()
endif()
elseif(FreeBSD)
elseif(FreeBSD AND NOT DragonFly)
set(CPACK_FREEBSD_PACKAGE_LICENSE "MIT")
set(CPACK_GENERATOR "${CPACK_GENERATOR};FREEBSD")
endif()
+3 -1
View File
@@ -13,7 +13,9 @@
[![Ask DeepWiki](https://deepwiki.com/badge.svg)](https://deepwiki.com/fastfetch-cli/fastfetch)
[![中文README](https://img.shields.io/badge/%E4%B8%AD%E6%96%87-README-red)](README-cn.md)
Fastfetch is a [neofetch](https://github.com/dylanaraps/neofetch)-like tool for fetching system information and displaying it in a visually appealing way. It is written mainly in C, with a focus on performance and customizability. Currently, it supports Linux, macOS, Windows 7+, Android, FreeBSD, OpenBSD, NetBSD, DragonFly, Haiku, and illumos (SunOS).
Fastfetch is a [neofetch](https://github.com/dylanaraps/neofetch)-like tool for fetching system information and displaying it in a visually appealing way. It is written mainly in C, with a focus on performance and customizability. Currently, it supports Linux, macOS, Windows 8.1+, Android, FreeBSD, OpenBSD, NetBSD, DragonFly, Haiku and SunOS (illumos, Solaris).
> Note: Fastfetch is only actively tested on x86-64 and aarch64 platforms. It may work on other platforms but is not guaranteed to do so.
<img src="screenshots/example1.png" width="49%" align="left" />
<img src="https://upload.wikimedia.org/wikipedia/commons/2/24/Transparent_Square_Tiles_Texture.png" width="49%" height="16px" align="left" />
+6
View File
@@ -1,3 +1,9 @@
fastfetch (2.58.0~#UBUNTU_CODENAME#) #UBUNTU_CODENAME#; urgency=medium
* Update to 2.58.0
-- Carter Li <zhangsongcui@live.cn> Thu, 22 Jan 2026 15:30:58 +0800
fastfetch (2.57.1~#UBUNTU_CODENAME#) #UBUNTU_CODENAME#; urgency=medium
* Update to 2.57.1
+33 -14
View File
@@ -318,7 +318,7 @@
"type": "string"
},
"cpuFormat": {
"description": "Output format of the module `CPU`. See Wiki for formatting syntax\n 1. {name}: Name\n 2. {vendor}: Vendor\n 3. {cores-physical}: Physical core count\n 4. {cores-logical}: Logical core count\n 5. {cores-online}: Online core count\n 6. {freq-base}: Base frequency (formatted)\n 7. {freq-max}: Max frequency (formatted)\n 8. {temperature}: Temperature (formatted)\n 9. {core-types}: Logical core count grouped by frequency\n 10. {packages}: Processor package count\n 11. {march}: X86-64 CPU microarchitecture",
"description": "Output format of the module `CPU`. See Wiki for formatting syntax\n 1. {name}: Name\n 2. {vendor}: Vendor\n 3. {cores-physical}: Physical core count\n 4. {cores-logical}: Logical core count\n 5. {cores-online}: Online core count\n 6. {freq-base}: Base frequency (formatted)\n 7. {freq-max}: Max frequency (formatted)\n 8. {temperature}: Temperature (formatted)\n 9. {core-types}: Logical core count grouped by frequency\n 10. {packages}: Processor package count\n 11. {march}: CPU microarchitecture\n 12. {numa-nodes}: NUMA node count",
"type": "string"
},
"cpucacheFormat": {
@@ -342,7 +342,7 @@
"type": "string"
},
"displayFormat": {
"description": "Output format of the module `Display`. See Wiki for formatting syntax\n 1. {width}: Screen configured width (in pixels)\n 2. {height}: Screen configured height (in pixels)\n 3. {refresh-rate}: Screen configured refresh rate (in Hz)\n 4. {scaled-width}: Screen scaled width (in pixels)\n 5. {scaled-height}: Screen scaled height (in pixels)\n 6. {name}: Screen name\n 7. {type}: Screen type (Built-in or External)\n 8. {rotation}: Screen rotation (in degrees)\n 9. {is-primary}: True if being the primary screen\n 10. {physical-width}: Screen physical width (in millimeters)\n 11. {physical-height}: Screen physical height (in millimeters)\n 12. {inch}: Physical diagonal length in inches\n 13. {ppi}: Pixels per inch (PPI)\n 14. {bit-depth}: Bits per color channel\n 15. {hdr-enabled}: True if high dynamic range (HDR) mode is enabled\n 16. {manufacture-year}: Year of manufacturing\n 17. {manufacture-week}: Nth week of manufacturing in the year\n 18. {serial}: Serial number\n 19. {platform-api}: The platform API used when detecting the display\n 20. {hdr-compatible}: True if the display is HDR compatible\n 21. {scale-factor}: HiDPI scale factor\n 22. {preferred-width}: Screen preferred width (in pixels)\n 23. {preferred-height}: Screen preferred height (in pixels)\n 24. {preferred-refresh-rate}: Screen preferred refresh rate (in Hz)",
"description": "Output format of the module `Display`. See Wiki for formatting syntax\n 1. {width}: Screen configured width (in pixels)\n 2. {height}: Screen configured height (in pixels)\n 3. {refresh-rate}: Screen configured refresh rate (in Hz)\n 4. {scaled-width}: Screen scaled width (in pixels)\n 5. {scaled-height}: Screen scaled height (in pixels)\n 6. {name}: Screen name\n 7. {type}: Screen type (Built-in or External)\n 8. {rotation}: Screen rotation (in degrees)\n 9. {is-primary}: True if being the primary screen\n 10. {physical-width}: Screen physical width (in millimeters)\n 11. {physical-height}: Screen physical height (in millimeters)\n 12. {inch}: Physical diagonal length in inches\n 13. {ppi}: Pixels per inch (PPI)\n 14. {bit-depth}: Bits per color channel\n 15. {hdr-enabled}: True if high dynamic range (HDR) mode is enabled\n 16. {manufacture-year}: Year of manufacturing\n 17. {manufacture-week}: Nth week of manufacturing in the year\n 18. {serial}: Serial number\n 19. {platform-api}: The platform API used when detecting the display\n 20. {hdr-compatible}: True if the display is HDR compatible\n 21. {scale-factor}: HiDPI scale factor\n 22. {preferred-width}: Screen preferred width (in pixels)\n 23. {preferred-height}: Screen preferred height (in pixels)\n 24. {preferred-refresh-rate}: Screen preferred refresh rate (in Hz)\n 25. {dpi}: DPI",
"type": "string"
},
"diskFormat": {
@@ -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. {nix-all}: Total number of all nix packages\n 44. {flatpak-all}: Total number of all flatpak app packages\n 45. {brew-all}: Total number of all brew packages\n 46. {guix-all}: Total number of all guix packages\n 47. {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": {
@@ -450,7 +450,7 @@
"type": "string"
},
"physicalmemoryFormat": {
"description": "Output format of the module `PhysicalMemory`. See Wiki for formatting syntax\n 1. {bytes}: Size (in bytes)\n 2. {size}: Size formatted\n 3. {max-speed}: Max speed (in MT/s)\n 4. {running-speed}: Running speed (in MT/s)\n 5. {type}: Type (DDR4, DDR5, etc.)\n 6. {form-factor}: Form factor (SODIMM, DIMM, etc.)\n 7. {locator}: Bank/Device Locator (BANK0/SIMM0, BANK0/SIMM1, etc.)\n 8. {vendor}: Vendor\n 9. {serial}: Serial number\n 10. {part-number}: Part number\n 11. {is-ecc-enabled}: True if ECC enabled",
"description": "Output format of the module `PhysicalMemory`. See Wiki for formatting syntax\n 1. {bytes}: Size (in bytes)\n 2. {size}: Size formatted\n 3. {max-speed}: Max speed (in MT/s)\n 4. {running-speed}: Running speed (in MT/s)\n 5. {type}: Type (DDR4, DDR5, etc.)\n 6. {form-factor}: Form factor (SODIMM, DIMM, etc.)\n 7. {locator}: Bank/Device Locator (BANK0/SIMM0, BANK0/SIMM1, etc.)\n 8. {vendor}: Vendor\n 9. {serial}: Serial number\n 10. {part-number}: Part number\n 11. {is-ecc-enabled}: True if ECC enabled\n 12. {is-installed}: True if a memory module is installed in the slot",
"type": "string"
},
"playerFormat": {
@@ -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",
"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": {
@@ -546,7 +546,7 @@
"type": "string"
},
"zpoolFormat": {
"description": "Output format of the module `Zpool`. See Wiki for formatting syntax\n 1. {name}: Zpool name\n 2. {state}: Zpool state\n 3. {used}: Size used\n 4. {total}: Size total\n 5. {used-percentage}: Size percentage num\n 6. {fragmentation-percentage}: Fragmentation percentage num\n 7. {used-percentage-bar}: Size percentage bar\n 8. {fragmentation-percentage-bar}: Fragmentation percentage bar",
"description": "Output format of the module `Zpool`. See Wiki for formatting syntax\n 1. {name}: Zpool name\n 2. {guid}: Zpool guid\n 3. {state}: Zpool state\n 4. {used}: Size used\n 5. {allocated}: Size allocated\n 6. {total}: Size total\n 7. {used-percentage}: Size used percentage num\n 8. {allocated-percentage}: Size allocated percentage num\n 9. {fragmentation-percentage}: Fragmentation percentage num\n 10. {used-percentage-bar}: Size used percentage bar\n 11. {allocated-percentage-bar}: Size allocated percentage bar\n 12. {fragmentation-percentage-bar}: Fragmentation percentage bar\n 13. {is-readonly}: Is read-only",
"type": "string"
}
},
@@ -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
},
@@ -2044,7 +2044,7 @@
"default": 0
},
"block": {
"description": "Set behavior of block printing",
"description": "Set behavior of block printing. Only works when symbol is set to `block` or `background`",
"type": "object",
"additionalProperties": false,
"properties": {
@@ -2067,6 +2067,25 @@
}
}
},
"brightness": {
"description": "Set the brightness of colors. Works together with the block color range to determine which colors are displayed",
"type": "string",
"oneOf": [
{
"const": "default",
"description": "Use the default setting (block/background: 0-15, others: 8-1)"
},
{
"const": "light",
"description": "Use ANSI bright foreground colors (codes 90-97, not color indices 9-15)"
},
{
"const": "normal",
"description": "Use normal ANSI colors (30-37; standard foreground colors, corresponding to color indices 0-7)"
}
],
"default": "default"
},
"key": {
"$ref": "#/$defs/key"
},
@@ -2518,11 +2537,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"
},
@@ -3655,6 +3669,11 @@
"const": "physicalmemory",
"description": "Print system physical memory devices"
},
"showEmptySlots": {
"description": "Set if uninstalled memory slots should be printed",
"type": "boolean",
"default": false
},
"key": {
"$ref": "#/$defs/key"
},
@@ -4487,7 +4506,7 @@
"detectPlugin": {
"description": "Set if window manager plugin should be detected on supported platforms",
"type": "boolean",
"default": false
"default": true
},
"key": {
"$ref": "#/$defs/key"
+51 -50
View File
@@ -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": ""
},
+57
View File
@@ -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"
}
]
}
+3
View File
@@ -6,6 +6,9 @@ def main(amdgpu_ids_path: str):
with open(amdgpu_ids_path, 'r') as f:
full_text = f.read()
if full_text == '':
sys.exit(f'Error: {amdgpu_ids_path} file is empty')
products = []
for line in full_text.split('\n'):
if not line or line[0] == '#' or not ',\t' in line:
+3
View File
@@ -18,6 +18,9 @@ def main(keep_vendor_list: set, pci_ids_path: str):
with open(pci_ids_path, 'r') as f:
full_text = f.read()
if full_text == '':
sys.exit(f'Error: {pci_ids_path} file is empty')
dev_list_text = full_text[:full_text.rfind('\n\n\n')] # remove known classes
for line in dev_list_text.split('\n'):
if not line or line[0] == '#':
+7
View File
@@ -19,7 +19,14 @@ 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
uint32_t uid;
#else
FFstrbuf sid;
#endif
FFstrbuf userName;
FFstrbuf fullUserName;
FFstrbuf hostName;
+9
View File
@@ -106,6 +106,15 @@ static inline void ffListClear(FFlist* list)
list->length = 0;
}
static inline void ffListReserve(FFlist* list, uint32_t newCapacity)
{
if (__builtin_expect(newCapacity <= list->capacity, false))
return;
list->data = (uint8_t*) realloc(list->data, (size_t) newCapacity * list->elementSize);
list->capacity = newCapacity;
}
#define FF_LIST_FOR_EACH(itemType, itemVarName, listVar) \
assert(sizeof(itemType) == (listVar).elementSize); \
for(itemType* itemVarName = (itemType*)(listVar).data; \
+4
View File
@@ -230,6 +230,7 @@ static inline void ffStrbufInitMoveS(FFstrbuf* strbuf, char* heapStr)
ffStrbufInitMoveNS(strbuf, (uint32_t) strlen(heapStr), heapStr);
}
// Despite the name, this function resets strbuf to the initial/unallocated state
static inline void ffStrbufDestroy(FFstrbuf* strbuf)
{
if(strbuf->allocated > 0)
@@ -627,4 +628,7 @@ static inline bool ffStrbufSeparatedContainIgnCase(const FFstrbuf* strbuf, const
return ffStrbufSeparatedContainIgnCaseNS(strbuf, comp->length, comp->chars, separator);
}
// Returns true if the strbuf is modified
bool ffStrbufDecodeHexEscapeSequences(FFstrbuf* strbuf);
#define FF_STRBUF_AUTO_DESTROY FFstrbuf __attribute__((__cleanup__(ffStrbufDestroy)))
+2 -2
View File
@@ -166,7 +166,7 @@ static const char *smcReadValue(io_connect_t conn, const UInt32Char_t key, doubl
case 1: *value = *(uint8_t *)(val.bytes); break;
case 2: *value = ntohs(*(uint16_t *)(val.bytes)); break;
case 4: *value = ntohl(*(uint32_t *)(val.bytes)); break;
case 8: *value = ntohll(*(uint64_t *)(val.bytes)); break;
case 8: *value = (double) ntohll(*(uint64_t *)(val.bytes)); break;
default:
return "Unsupported SMC unsigned integer data size";
}
@@ -213,7 +213,7 @@ static const char *smcReadValue(io_connect_t conn, const UInt32Char_t key, doubl
case 1: *value = *(int8_t *)(val.bytes); break;
case 2: *value = ntohs(*(int16_t *)(val.bytes)); break;
case 4: *value = ntohl(*(int32_t *)(val.bytes)); break;
case 8: *value = ntohll(*(int64_t *)(val.bytes)); break;
case 8: *value = (double)ntohll(*(int64_t *)(val.bytes)); break;
default: return "Unsupported SMC signed integer data size";
}
}
+45
View File
@@ -0,0 +1,45 @@
#pragma once
#include "common/FFstrbuf.h"
// For ffRegReadValue(s)
// If length is 0, data will be allocated with malloc() and must be freed by the caller
// Otherwise, data must point to a buffer of at least length bytes, and the actual length of the data will be written to length.
typedef struct FFArgBuffer
{
void* data;
uint32_t length;
} FFArgBuffer;
typedef enum __attribute__((__packed__)) FFArgType
{
FF_ARG_TYPE_NULL = 0,
FF_ARG_TYPE_UINT,
FF_ARG_TYPE_UINT64,
FF_ARG_TYPE_UINT16,
FF_ARG_TYPE_UINT8,
FF_ARG_TYPE_INT,
FF_ARG_TYPE_STRING,
FF_ARG_TYPE_STRBUF,
FF_ARG_TYPE_FLOAT,
FF_ARG_TYPE_DOUBLE,
FF_ARG_TYPE_LIST,
FF_ARG_TYPE_BOOL,
FF_ARG_TYPE_BUFFER,
} FFArgType;
#define FF_ARG(variable, var_name) { _Generic((variable), \
uint32_t: FF_ARG_TYPE_UINT, \
uint64_t: FF_ARG_TYPE_UINT64, \
uint16_t: FF_ARG_TYPE_UINT16, \
uint8_t: FF_ARG_TYPE_UINT8, \
int32_t: FF_ARG_TYPE_INT, \
char*: FF_ARG_TYPE_STRING, \
const char*: FF_ARG_TYPE_STRING, \
FFstrbuf: FF_ARG_TYPE_STRBUF, \
float: FF_ARG_TYPE_FLOAT, \
double: FF_ARG_TYPE_DOUBLE, \
FFlist: FF_ARG_TYPE_LIST, \
bool: FF_ARG_TYPE_BOOL, \
FFArgBuffer: FF_ARG_TYPE_BUFFER \
), _Generic((variable), char*: (variable), const char*: (variable), default: &(variable) ), (var_name) }
+3 -1
View File
@@ -3,7 +3,9 @@
#include <assert.h>
#ifdef __has_builtin
#if __has_builtin(__is_array)
#if !__cplusplus && FF_SUPPORTS_COUNT_OF
#define ARRAY_SIZE(x) _Countof(x)
#elif __has_builtin(__is_array)
#define ARRAY_SIZE(x) ({ static_assert(__is_array(__typeof__(x)), "Must be an array"); (uint32_t) (sizeof(x) / sizeof(*(x))); })
#elif __has_builtin(__builtin_types_compatible_p)
#define ARRAY_SIZE(x) ({ static_assert(!__builtin_types_compatible_p(__typeof__(x), __typeof__(&*(x))), "Must not be a pointer"); (uint32_t) (sizeof(x) / sizeof(*(x))); })
+4
View File
@@ -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
+3 -1
View File
@@ -29,5 +29,7 @@ static inline const char* ffFindFileName(const char* file)
#define FF_DEBUG(format, ...) FF_DEBUG_PRINT(__FILE__, __LINE__, format, ##__VA_ARGS__)
#if _WIN32
const char* ffDebugWin32Error(unsigned long errorCode);
const char* ffDebugWin32Error(DWORD errorCode);
const char* ffDebugNtStatus(NTSTATUS status);
const char* ffDebugHResult(HRESULT hr);
#endif
+1
View File
@@ -10,3 +10,4 @@ void ffEdidGetPhysicalResolution(const uint8_t edid[128], uint32_t* width, uint3
void ffEdidGetPhysicalSize(const uint8_t edid[128], uint32_t* width, uint32_t* height); // in mm
void ffEdidGetSerialAndManufactureDate(const uint8_t edid[128], uint32_t* serial, uint16_t* year, uint16_t* week);
bool ffEdidGetHdrCompatible(const uint8_t* edid, uint32_t length);
bool ffEdidIsValid(const uint8_t edid[128], uint32_t length);
+2 -33
View File
@@ -1,41 +1,10 @@
#pragma once
#include "common/FFstrbuf.h"
typedef enum __attribute__((__packed__)) FFformatArgType
{
FF_FORMAT_ARG_TYPE_NULL = 0,
FF_FORMAT_ARG_TYPE_UINT,
FF_FORMAT_ARG_TYPE_UINT64,
FF_FORMAT_ARG_TYPE_UINT16,
FF_FORMAT_ARG_TYPE_UINT8,
FF_FORMAT_ARG_TYPE_INT,
FF_FORMAT_ARG_TYPE_STRING,
FF_FORMAT_ARG_TYPE_STRBUF,
FF_FORMAT_ARG_TYPE_FLOAT,
FF_FORMAT_ARG_TYPE_DOUBLE,
FF_FORMAT_ARG_TYPE_LIST,
FF_FORMAT_ARG_TYPE_BOOL
} FFformatArgType;
#define FF_FORMAT_ARG(variable, var_name) { _Generic((variable), \
uint32_t: FF_FORMAT_ARG_TYPE_UINT, \
uint64_t: FF_FORMAT_ARG_TYPE_UINT64, \
uint16_t: FF_FORMAT_ARG_TYPE_UINT16, \
uint8_t: FF_FORMAT_ARG_TYPE_UINT8, \
int32_t: FF_FORMAT_ARG_TYPE_INT, \
char*: FF_FORMAT_ARG_TYPE_STRING, \
const char*: FF_FORMAT_ARG_TYPE_STRING, \
FFstrbuf: FF_FORMAT_ARG_TYPE_STRBUF, \
float: FF_FORMAT_ARG_TYPE_FLOAT, \
double: FF_FORMAT_ARG_TYPE_DOUBLE, \
FFlist: FF_FORMAT_ARG_TYPE_LIST, \
bool: FF_FORMAT_ARG_TYPE_BOOL \
), _Generic((variable), char*: (variable), const char*: (variable), default: &(variable) ), (var_name) }
#include "common/argType.h"
typedef struct FFformatarg
{
FFformatArgType type;
FFArgType type;
const void* value;
const char* name; // argument name, must start with an alphabet
} FFformatarg;
+10
View File
@@ -10,12 +10,17 @@ 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);
ffStrbufInit(&platform->hostName);
ffStrbufInit(&platform->userShell);
#ifdef _WIN32
ffStrbufInit(&platform->sid);
#endif
FFPlatformSysinfo* info = &platform->sysinfo;
ffStrbufInit(&info->name);
@@ -45,12 +50,17 @@ void ffPlatformDestroy(FFPlatform* platform)
ffStrbufDestroy(dir);
ffListDestroy(&platform->dataDirs);
ffStrbufDestroy(&platform->exePath);
ffStrbufDestroy(&platform->cwd);
ffStrbufDestroy(&platform->userName);
ffStrbufDestroy(&platform->hostName);
ffStrbufDestroy(&platform->userShell);
ffStrbufDestroy(&platform->fullUserName);
#ifdef _WIN32
ffStrbufDestroy(&platform->sid);
#endif
FFPlatformSysinfo* info = &platform->sysinfo;
ffStrbufDestroy(&info->architecture);
ffStrbufDestroy(&info->name);
+108 -13
View File
@@ -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 <paths.h>
#ifdef __APPLE__
#include <libproc.h>
#include <mach-o/dyld.h>
#include <sys/sysctl.h>
#elif defined(__FreeBSD__) || defined(__OpenBSD__) || defined(__NetBSD__)
#elif defined(__FreeBSD__) || defined(__NetBSD__)
#include <sys/sysctl.h>
#elif defined(__OpenBSD__)
#include <sys/sysctl.h>
#include <kvm.h>
#include "common/path.h"
#elif defined(__HAIKU__)
#include <image.h>
#include <OS.h>
@@ -28,15 +33,19 @@ static void getExePath(FFPlatform* platform)
if (exePathLen >= 0)
exePath[exePathLen] = '\0';
#elif defined(__APPLE__)
int exePathLen = proc_pidpath((int) getpid(), 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(
(int[]){CTL_KERN,
#ifdef __FreeBSD__
KERN_PROC, KERN_PROC_PATHNAME, (int) getpid()
KERN_PROC, KERN_PROC_PATHNAME, (pid_t) platform->pid
#else
KERN_PROC_ARGS, (int) getpid(), KERN_PROC_PATHNAME
KERN_PROC_ARGS, platform->pid, KERN_PROC_PATHNAME
#endif
}, 4,
exePath, &exePathLen,
@@ -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)
@@ -177,17 +260,16 @@ static void getDataDirs(FFPlatform* platform)
static void getUserName(FFPlatform* platform, const struct passwd* pwd)
{
const char* user = getenv("USER");
if(!ffStrSet(user) && pwd)
user = pwd->pw_name;
ffStrbufAppendS(&platform->userName, user);
if (pwd)
{
ffStrbufAppendS(&platform->fullUserName, pwd->pw_gecos);
ffStrbufSetS(&platform->userName, pwd->pw_name);
ffStrbufSetS(&platform->fullUserName, pwd->pw_gecos);
ffStrbufTrimSpace(&platform->fullUserName);
}
else
{
ffStrbufSetS(&platform->userName, getenv("USER"));
}
}
static void getHostName(FFPlatform* platform, const struct utsname* uts)
@@ -225,15 +307,28 @@ 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)
{
struct passwd* pwd = getpwuid(getuid());
platform->pid = (uint32_t) getpid();
platform->uid = getuid();
struct passwd* pwd = getpwuid(platform->uid);
struct utsname uts;
if(uname(&uts) < 0)
memset(&uts, 0, sizeof(uts));
getExePath(platform);
getCwd(platform);
getHomeDir(platform, pwd);
getCacheDir(platform);
getConfigDirs(platform);
+80 -57
View File
@@ -6,8 +6,10 @@
#include "common/windows/registry.h"
#include "common/windows/nt.h"
#include <stdalign.h>
#include <windows.h>
#include <shlobj.h>
#include <sddl.h>
#define SECURITY_WIN32 1 // For secext.h
#include <secext.h>
@@ -15,20 +17,32 @@
static void getExePath(FFPlatform* platform)
{
wchar_t exePathW[MAX_PATH];
DWORD exePathWLen = GetModuleFileNameW(NULL, exePathW, MAX_PATH);
if (exePathWLen == 0 || exePathWLen >= MAX_PATH) return;
FF_AUTO_CLOSE_FD HANDLE hPath = CreateFileW(exePathW, GENERIC_READ, FILE_SHARE_READ, NULL, OPEN_EXISTING, FILE_FLAG_BACKUP_SEMANTICS, NULL);
FF_AUTO_CLOSE_FD HANDLE hPath = CreateFileW(
ffGetPeb()->ProcessParameters->ImagePathName.Buffer,
GENERIC_READ,
FILE_SHARE_READ,
NULL,
OPEN_EXISTING,
FILE_FLAG_BACKUP_SEMANTICS,
NULL);
if (hPath != INVALID_HANDLE_VALUE)
{
DWORD len = GetFinalPathNameByHandleW(hPath, exePathW, MAX_PATH, FILE_NAME_OPENED);
DWORD len = GetFinalPathNameByHandleW(hPath, exePathW, MAX_PATH, FILE_NAME_NORMALIZED);
if (len > 0 && len < MAX_PATH)
exePathWLen = len;
{
ffStrbufSetNWS(&platform->exePath, len, exePathW);
if (ffStrbufStartsWithS(&platform->exePath, "\\\\?\\"))
ffStrbufSubstrAfter(&platform->exePath, 3);
}
}
if (platform->exePath.length == 0)
{
PCUNICODE_STRING imagePathName = &ffGetPeb()->ProcessParameters->ImagePathName;
ffStrbufSetNWS(&platform->exePath, imagePathName->Length / sizeof(wchar_t), imagePathName->Buffer);
}
ffStrbufSetNWS(&platform->exePath, exePathWLen, exePathW);
if (ffStrbufStartsWithS(&platform->exePath, "\\\\?\\"))
ffStrbufSubstrAfter(&platform->exePath, 3);
ffStrbufReplaceAllC(&platform->exePath, '\\', '/');
}
@@ -70,15 +84,15 @@ static void getCacheDir(FFPlatform* platform)
static void platformPathAddKnownFolder(FFlist* dirs, REFKNOWNFOLDERID folderId)
{
PWSTR pPath = NULL;
if(SUCCEEDED(SHGetKnownFolderPath(folderId, 0, NULL, &pPath)))
if (SUCCEEDED(SHGetKnownFolderPath(folderId, KF_FLAG_DEFAULT, NULL, &pPath)))
{
FF_STRBUF_AUTO_DESTROY buffer = ffStrbufCreateWS(pPath);
CoTaskMemFree(pPath);
ffStrbufReplaceAllC(&buffer, '\\', '/');
ffStrbufEnsureEndsWithC(&buffer, '/');
if (!ffListContains(dirs, &buffer, (void*) ffStrbufEqual))
ffStrbufInitMove((FFstrbuf*) ffListAdd(dirs), &buffer);
}
CoTaskMemFree(pPath);
}
static void platformPathAddEnvSuffix(FFlist* dirs, const char* env, const char* suffix)
@@ -136,21 +150,25 @@ static void getDataDirs(FFPlatform* platform)
static void getUserName(FFPlatform* platform)
{
const char* userName = getenv("USERNAME");
if (ffStrSet(userName))
ffStrbufSetS(&platform->userName, userName);
else
{
wchar_t buffer[256];
DWORD len = ARRAY_SIZE(buffer);
if(GetUserNameW(buffer, &len))
ffStrbufSetWS(&platform->userName, buffer);
}
wchar_t buffer[256];
DWORD len = ARRAY_SIZE(buffer);
if (GetUserNameExW(NameDisplay, buffer, &len))
DWORD size = ARRAY_SIZE(buffer);
if (GetUserNameExW(NameDisplay, buffer, &size))
ffStrbufSetWS(&platform->fullUserName, buffer);
size = ARRAY_SIZE(buffer);
if (GetUserNameW(buffer, &size)) // GetUserNameExW(10002)?
ffStrbufSetWS(&platform->userName, buffer);
else
ffStrbufSetS(&platform->userName, getenv("USERNAME"));
alignas(TOKEN_USER) char buf[SECURITY_MAX_SID_SIZE + sizeof(TOKEN_USER)];
if (NT_SUCCESS(NtQueryInformationToken(NtCurrentProcessToken(), TokenUser, buf, sizeof(buf), &size)))
{
TOKEN_USER* tokenUser = (TOKEN_USER*) buf;
UNICODE_STRING sidString = { .Buffer = buffer, .Length = 0, .MaximumLength = sizeof(buffer) };
if (NT_SUCCESS(RtlConvertSidToUnicodeString(&sidString, tokenUser->User.Sid, FALSE)))
ffStrbufSetNWS(&platform->sid, sidString.Length / sizeof(wchar_t), sidString.Buffer);
}
}
static void getHostName(FFPlatform* platform)
@@ -181,7 +199,7 @@ static void getUserShell(FFPlatform* platform)
static const char* detectWine(void)
{
const char * __cdecl wine_get_version(void);
HMODULE hntdll = GetModuleHandleW(L"ntdll.dll");
void* hntdll = ffLibraryGetModule(L"ntdll.dll");
if (!hntdll) return NULL;
FF_LIBRARY_LOAD_SYMBOL_LAZY(hntdll, wine_get_version);
if (!ffwine_get_version) return NULL;
@@ -190,53 +208,48 @@ 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;
FF_AUTO_CLOSE_FD HANDLE hKey = NULL;
if(!ffRegOpenKeyForRead(HKEY_LOCAL_MACHINE, L"SOFTWARE\\Microsoft\\Windows NT\\CurrentVersion", &hKey, NULL))
return;
uint32_t ubr = 0;
ffRegReadUint(hKey, L"UBR", &ubr, NULL);
ffRegReadValues(hKey, 2, (FFRegValueArg[]) {
FF_ARG(ubr, L"UBR"),
FF_ARG(info->version, L"BuildLabEx"),
}, NULL);
ffStrbufAppendF(&info->release,
PPEB_FULL peb = ffGetPeb();
ffStrbufSetF(&info->release,
"%u.%u.%u.%u",
(unsigned) osVersion.dwMajorVersion,
(unsigned) osVersion.dwMinorVersion,
(unsigned) osVersion.dwBuildNumber,
(unsigned) peb->OSMajorVersion,
(unsigned) peb->OSMinorVersion,
(unsigned) peb->OSBuildNumber,
(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;
}
}
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;
@@ -244,7 +257,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");
@@ -282,14 +295,23 @@ static void getSystemArchitectureAndPageSize(FFPlatformSysinfo* info)
default:
ffStrbufSetStatic(&info->architecture, "unknown");
break;
}
}
}
info->pageSize = sysInfo.dwPageSize;
static void getCwd(FFPlatform* platform)
{
PCURDIR cwd = &ffGetPeb()->ProcessParameters->CurrentDirectory;
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) (uintptr_t) ffGetTeb()->ClientId.UniqueProcess;
getExePath(platform);
getCwd(platform);
getHomeDir(platform);
getCacheDir(platform);
getConfigDirs(platform);
@@ -300,5 +322,6 @@ void ffPlatformInitImpl(FFPlatform* platform)
getUserShell(platform);
getSystemReleaseAndVersion(&platform->sysinfo);
getSystemArchitectureAndPageSize(&platform->sysinfo);
getSystemArchitecture(&platform->sysinfo);
getSystemPageSize(&platform->sysinfo);
}
+1 -5
View File
@@ -6,11 +6,7 @@
void* ffListAdd(FFlist* list)
{
if(list->length == list->capacity)
{
list->capacity = list->capacity == 0 ? FF_LIST_DEFAULT_ALLOC : list->capacity * 2;
// realloc(NULL, newSize) is same as malloc(newSize)
list->data = realloc(list->data, (size_t)list->capacity * list->elementSize);
}
ffListReserve(list, list->capacity == 0 ? FF_LIST_DEFAULT_ALLOC : list->capacity * 2);
++list->length;
return ffListGet(list, list->length - 1);
+43 -1
View File
@@ -1,5 +1,6 @@
#include "common/FFstrbuf.h"
#include "common/mallocHelper.h"
#include "common/stringUtils.h"
#include <ctype.h>
#include <inttypes.h>
@@ -183,7 +184,7 @@ const char* ffStrbufAppendSUntilC(FFstrbuf* strbuf, const char* value, char unti
if(value == NULL)
return NULL;
char* end = strchr(value, until);
const char* end = strchr(value, until);
if(end == NULL)
ffStrbufAppendS(strbuf, value);
else
@@ -915,3 +916,44 @@ bool ffStrbufSeparatedContainIgnCaseNS(const FFstrbuf* strbuf, uint32_t compLeng
return false;
}
bool ffStrbufDecodeHexEscapeSequences(FFstrbuf* strbuf)
{
assert(strbuf);
if (strbuf->length < 4)
return false;
// Static string must be converted first.
assert(strbuf->allocated > 0);
bool changed = false;
uint32_t read = 0;
uint32_t write = 0;
while (read < strbuf->length)
{
if (
read + 3 < strbuf->length &&
strbuf->chars[read] == '\\' &&
strbuf->chars[read + 1] == 'x'
)
{
int8_t hi = ffHexCharToInt(strbuf->chars[read + 2]);
int8_t lo = ffHexCharToInt(strbuf->chars[read + 3]);
if (hi >= 0 && lo >= 0)
{
strbuf->chars[write++] = (char) ((hi << 4) | lo);
read += 4;
changed = true;
continue;
}
}
strbuf->chars[write++] = strbuf->chars[read++];
}
strbuf->length = write;
strbuf->chars[write] = '\0';
return changed;
}
+25 -10
View File
@@ -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 <windows.h>
#include <imagehlp.h>
#include <stdlib.h>
#include <string.h>
@@ -17,19 +16,34 @@
*/
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 hSection = NULL;
if (!NT_SUCCESS(NtCreateSection(&hSection, SECTION_MAP_READ, NULL, NULL, PAGE_READONLY, SEC_COMMIT, hFile)))
return "NtCreateSection() failed";
PVOID base = NULL;
SIZE_T viewSize = 0;
if (!NT_SUCCESS(NtMapViewOfSection(hSection, NtCurrentProcess(), &base, 0, 0, NULL, &viewSize, ViewUnmap, 0, PAGE_READONLY)))
return "NtMapViewOfSection() failed";
PIMAGE_NT_HEADERS ntHeaders = RtlImageNtHeader(base);
if (!ntHeaders)
{
NtUnmapViewOfSection(NtCurrentProcess(), 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 +62,6 @@ const char* ffBinaryExtractStrings(const char *peFile, bool (*cb)(const char *st
}
}
NtUnmapViewOfSection(NtCurrentProcess(), base);
return NULL;
}
+1 -1
View File
@@ -61,7 +61,7 @@ bool ffParseModuleOptions(const char* key, const char* value)
}
fprintf(stderr, "Error: Unsupported module option: %s\n", key);
fputs(" Support of module options has been removed. Please add the flag to the JSON config instead.\n", stderr);
fprintf(stderr, " Example (demonstration only): `{ \"modules\": [ { \"type\": \"%s\", \"%s\": %s%s%s } ] }`\n", moduleName.chars, jsonKey.chars, value ? "\"" : "", value ? value : "true", value ? "\"" : "");
fprintf(stderr, " Example (demonstration only): `{ \"modules\": [ { \"type\": \"%s\", \"%s\": %s%s%s } ] }`\n", moduleName.chars, jsonKey.chars, value ? "\"" : "", value ?: "true", value ? "\"" : "");
fputs(" See <https://github.com/fastfetch-cli/fastfetch/wiki/Configuration> for more information.\n", stderr);
exit(477);
}
+10
View File
@@ -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);
+26 -4
View File
@@ -10,7 +10,7 @@ const char* ffDebugWin32Error(DWORD errorCode)
FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_IGNORE_INSERTS,
NULL,
(DWORD) errorCode,
MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT),
0,
buffer,
sizeof(buffer),
NULL);
@@ -19,10 +19,32 @@ const char* ffDebugWin32Error(DWORD errorCode)
snprintf(buffer, sizeof(buffer), "Unknown error code (%lu)", errorCode);
} else {
// Remove trailing newline
if (buffer[len - 1] == '\n') buffer[len - 1] = '\0';
if (buffer[len - 2] == '\r') buffer[len - 2] = '\0';
snprintf(buffer + len - 2, sizeof(buffer) - len + 2, " (%lu)", errorCode);
while (len > 0 && (buffer[len - 1] == '\r' || buffer[len - 1] == '\n')) {
buffer[--len] = '\0';
}
snprintf(buffer + len, sizeof(buffer) - len + 2, " (%lu)", errorCode);
}
return buffer;
}
const char* ffDebugNtStatus(NTSTATUS status)
{
return ffDebugWin32Error(RtlNtStatusToDosError(status));
}
static inline DWORD HRESULTToWin32Error(HRESULT hr)
{
if (SUCCEEDED(hr))
return ERROR_SUCCESS;
if (HRESULT_FACILITY(hr) == FACILITY_WIN32)
return HRESULT_CODE(hr);
return ERROR_INTERNAL_ERROR;
}
const char* ffDebugHResult(HRESULT hr)
{
return ffDebugWin32Error(HRESULTToWin32Error(hr));
}
+13
View File
@@ -128,3 +128,16 @@ bool ffEdidGetHdrCompatible(const uint8_t* edid, uint32_t length)
}
return false;
}
bool ffEdidIsValid(const uint8_t edid[128], uint32_t length)
{
if (length < 128 || length % 128 != 0) return false;
static const uint8_t edidHeader[] = { 0x00, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x00 };
if (memcmp(edid, edidHeader, sizeof(edidHeader)) != 0) return false;
uint8_t sum = 0;
for (uint32_t i = 0; i < 128; i++) sum += edid[i];
return sum == 0;
}
+32 -25
View File
@@ -10,37 +10,37 @@ void ffFormatAppendFormatArg(FFstrbuf* buffer, const FFformatarg* formatarg)
{
switch(formatarg->type)
{
case FF_FORMAT_ARG_TYPE_INT:
case FF_ARG_TYPE_INT:
ffStrbufAppendSInt(buffer, *(int32_t*)formatarg->value);
break;
case FF_FORMAT_ARG_TYPE_UINT:
case FF_ARG_TYPE_UINT:
ffStrbufAppendUInt(buffer, *(uint32_t*)formatarg->value);
break;
case FF_FORMAT_ARG_TYPE_UINT64:
case FF_ARG_TYPE_UINT64:
ffStrbufAppendUInt(buffer, *(uint64_t*)formatarg->value);
break;
case FF_FORMAT_ARG_TYPE_UINT16:
case FF_ARG_TYPE_UINT16:
ffStrbufAppendUInt(buffer, *(uint16_t*)formatarg->value);
break;
case FF_FORMAT_ARG_TYPE_UINT8:
case FF_ARG_TYPE_UINT8:
ffStrbufAppendUInt(buffer, *(uint8_t*)formatarg->value);
break;
case FF_FORMAT_ARG_TYPE_STRING:
case FF_ARG_TYPE_STRING:
ffStrbufAppendS(buffer, (const char*)formatarg->value);
break;
case FF_FORMAT_ARG_TYPE_STRBUF:
case FF_ARG_TYPE_STRBUF:
ffStrbufAppend(buffer, (const FFstrbuf*)formatarg->value);
break;
case FF_FORMAT_ARG_TYPE_FLOAT:
case FF_ARG_TYPE_FLOAT:
ffStrbufAppendDouble(buffer, *(float*)formatarg->value, instance.config.display.fractionNdigits, instance.config.display.fractionTrailingZeros != FF_FRACTION_TRAILING_ZEROS_TYPE_NEVER);
break;
case FF_FORMAT_ARG_TYPE_DOUBLE:
case FF_ARG_TYPE_DOUBLE:
ffStrbufAppendDouble(buffer, *(double*)formatarg->value, instance.config.display.fractionNdigits, instance.config.display.fractionTrailingZeros != FF_FRACTION_TRAILING_ZEROS_TYPE_NEVER);
break;
case FF_FORMAT_ARG_TYPE_BOOL:
case FF_ARG_TYPE_BOOL:
ffStrbufAppendS(buffer, *(bool*)formatarg->value ? "true" : "false");
break;
case FF_FORMAT_ARG_TYPE_LIST:
case FF_ARG_TYPE_LIST:
{
const FFlist* list = (const FFlist*) formatarg->value;
for(uint32_t i = 0; i < list->length; i++)
@@ -51,8 +51,15 @@ void ffFormatAppendFormatArg(FFstrbuf* buffer, const FFformatarg* formatarg)
}
break;
}
case FF_ARG_TYPE_BUFFER:
{
// Placeholder for binary data, just print the size for now
const FFArgBuffer* argBuffer = (const FFArgBuffer*) formatarg->value;
ffStrbufAppendF(buffer, "buffer(%u bytes)", argBuffer->length);
break;
}
default:
if(formatarg->type != FF_FORMAT_ARG_TYPE_NULL)
if(formatarg->type != FF_ARG_TYPE_NULL)
fprintf(stderr, "Error: format string \"%s\": argument is not implemented: %i\n", buffer->chars, formatarg->type);
break;
}
@@ -87,7 +94,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;
}
}
@@ -107,16 +114,16 @@ static inline void appendInvalidPlaceholder(FFstrbuf* buffer, const char* start,
static inline bool formatArgSet(const FFformatarg* arg)
{
return arg->value != NULL && (
(arg->type == FF_FORMAT_ARG_TYPE_DOUBLE && *(double*)arg->value > 0.0) ||
(arg->type == FF_FORMAT_ARG_TYPE_FLOAT && *(float*)arg->value > 0.0) ||
(arg->type == FF_FORMAT_ARG_TYPE_INT && *(int*)arg->value > 0) ||
(arg->type == FF_FORMAT_ARG_TYPE_STRBUF && ((FFstrbuf*)arg->value)->length > 0) ||
(arg->type == FF_FORMAT_ARG_TYPE_STRING && ffStrSet((char*)arg->value)) ||
(arg->type == FF_FORMAT_ARG_TYPE_UINT8 && *(uint8_t*)arg->value > 0) ||
(arg->type == FF_FORMAT_ARG_TYPE_UINT16 && *(uint16_t*)arg->value > 0) ||
(arg->type == FF_FORMAT_ARG_TYPE_UINT && *(uint32_t*)arg->value > 0) ||
(arg->type == FF_FORMAT_ARG_TYPE_BOOL && *(bool*)arg->value) ||
(arg->type == FF_FORMAT_ARG_TYPE_LIST && ((FFlist*)arg->value)->length > 0)
(arg->type == FF_ARG_TYPE_DOUBLE && *(double*)arg->value > 0.0) ||
(arg->type == FF_ARG_TYPE_FLOAT && *(float*)arg->value > 0.0) ||
(arg->type == FF_ARG_TYPE_INT && *(int*)arg->value > 0) ||
(arg->type == FF_ARG_TYPE_STRBUF && ((FFstrbuf*)arg->value)->length > 0) ||
(arg->type == FF_ARG_TYPE_STRING && ffStrSet((char*)arg->value)) ||
(arg->type == FF_ARG_TYPE_UINT8 && *(uint8_t*)arg->value > 0) ||
(arg->type == FF_ARG_TYPE_UINT16 && *(uint16_t*)arg->value > 0) ||
(arg->type == FF_ARG_TYPE_UINT && *(uint32_t*)arg->value > 0) ||
(arg->type == FF_ARG_TYPE_BOOL && *(bool*)arg->value) ||
(arg->type == FF_ARG_TYPE_LIST && ((FFlist*)arg->value)->length > 0)
);
}
@@ -204,7 +211,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 +237,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;
+22 -16
View File
@@ -55,12 +55,15 @@ void ffInitInstance(void)
setlocale(LC_TIME, "");
#endif
initState(&instance.state);
defaultConfig();
initState(&instance.state);
}
static volatile bool ffDisableLinewrap = true;
static volatile bool ffHideCursor = true;
#if _WIN32
static volatile UINT oldCp = CP_UTF8;
#endif
static void resetConsole(void)
{
@@ -70,12 +73,15 @@ static void resetConsole(void)
if(ffHideCursor)
fputs("\033[?25h", stdout);
#if defined(_WIN32)
fflush(stdout);
#endif
if(instance.state.dynamicInterval > 0)
fputs("\033[?1049l", stdout); // Disable alternate buffer
#if defined(_WIN32)
fflush(stdout);
if(oldCp != CP_UTF8)
SetConsoleOutputCP(oldCp);
#endif
}
#ifdef _WIN32
@@ -106,9 +112,12 @@ void ffStart(void)
SetConsoleCtrlHandler(consoleHandler, TRUE);
HANDLE hStdout = GetStdHandle(STD_OUTPUT_HANDLE);
DWORD mode = 0;
GetConsoleMode(hStdout, &mode);
SetConsoleMode(hStdout, mode | ENABLE_PROCESSED_OUTPUT | ENABLE_VIRTUAL_TERMINAL_PROCESSING);
SetConsoleOutputCP(CP_UTF8);
if (GetConsoleMode(hStdout, &mode))
{
SetConsoleMode(hStdout, mode | ENABLE_PROCESSED_OUTPUT | ENABLE_VIRTUAL_TERMINAL_PROCESSING);
oldCp = GetConsoleOutputCP();
if (oldCp != CP_UTF8) SetConsoleOutputCP(CP_UTF8);
}
#else
if (instance.config.display.noBuffer) setvbuf(stdout, NULL, _IONBF, 0);
struct sigaction action = { .sa_handler = exitSignalHandler };
@@ -225,9 +234,6 @@ void ffListFeatures(void)
#if FF_HAVE_FREETYPE
"freetype\n"
#endif
#if FF_HAVE_PCIACCESS
"libpciaccess\n"
#endif
#if FF_HAVE_PULSE
"libpulse\n"
#endif
@@ -240,9 +246,6 @@ void ffListFeatures(void)
#if FF_HAVE_LIBZFS
"libzfs\n"
#endif
#if FF_HAVE_DIRECTX_HEADERS
"Directx Headers\n"
#endif
#if FF_USE_SYSTEM_YYJSON
"System yyjson\n"
#endif
@@ -255,8 +258,11 @@ void ffListFeatures(void)
#if FF_HAVE_EMBEDDED_PCIIDS
"Embedded pciids\n"
#endif
#if FF_WIN7_COMPAT
"Windows 7 Compatibility\n"
#if FF_WIN81_COMPAT
"Windows 8.1 Compatibility\n"
#endif
#if FF_APPLE_MEMSIZE_USABLE
"Apple memsize_usable\n"
#endif
""
, stdout);
+1 -37
View File
@@ -23,7 +23,7 @@ static void createSubfolders(const char* fileName)
{
FF_STRBUF_AUTO_DESTROY path = ffStrbufCreate();
char *token = NULL;
const char *token = NULL;
while((token = strchr(fileName, '/')) != NULL)
{
ffStrbufAppendNS(&path, (uint32_t)(token - fileName + 1), fileName);
@@ -97,42 +97,6 @@ bool ffAppendFDBuffer(int fd, FFstrbuf* buffer)
return buffer->length > 0;
}
ssize_t ffReadFileData(const char* fileName, size_t dataSize, void* data)
{
int FF_AUTO_CLOSE_FD fd = open(fileName, O_RDONLY | O_CLOEXEC);
if(fd == -1)
return -1;
return ffReadFDData(fd, dataSize, data);
}
ssize_t ffReadFileDataRelative(int dfd, const char* fileName, size_t dataSize, void* data)
{
int FF_AUTO_CLOSE_FD fd = openat(dfd, fileName, O_RDONLY | O_CLOEXEC);
if(fd == -1)
return -1;
return ffReadFDData(fd, dataSize, data);
}
bool ffAppendFileBuffer(const char* fileName, FFstrbuf* buffer)
{
int FF_AUTO_CLOSE_FD fd = open(fileName, O_RDONLY | O_CLOEXEC);
if(fd == -1)
return false;
return ffAppendFDBuffer(fd, buffer);
}
bool ffAppendFileBufferRelative(int dfd, const char* fileName, FFstrbuf* buffer)
{
int FF_AUTO_CLOSE_FD fd = openat(dfd, fileName, O_RDONLY | O_CLOEXEC);
if(fd == -1)
return false;
return ffAppendFDBuffer(fd, buffer);
}
bool ffPathExpandEnv(const char* in, FFstrbuf* out)
{
bool result = false;
+179 -68
View File
@@ -1,32 +1,172 @@
#include "common/io.h"
#include "fastfetch.h"
#include "common/io.h"
#include "common/stringUtils.h"
#include "common/windows/nt.h"
#include "common/windows/unicode.h"
#include <windows.h>
#include "common/windows/nt.h"
#include <ntstatus.h>
static void createSubfolders(const char* fileName)
static bool createSubfolders(wchar_t* fileName)
{
FF_STRBUF_AUTO_DESTROY path = ffStrbufCreate();
char *token = NULL;
while((token = strchr(fileName, '/')) != NULL)
HANDLE hRoot = ffGetPeb()->ProcessParameters->CurrentDirectory.Handle;
bool closeRoot = false;
wchar_t* ptr = fileName;
// Absolute drive path: C:\...
if (ffCharIsEnglishAlphabet((char)ptr[0]) && ptr[1] == L':' && ptr[2] == L'\\')
{
ffStrbufAppendNS(&path, (uint32_t)(token - fileName + 1), fileName);
CreateDirectoryA(path.chars, NULL);
fileName = token + 1;
wchar_t saved = ptr[3];
ptr[3] = L'\0';
hRoot = CreateFileW(
fileName,
FILE_LIST_DIRECTORY | FILE_TRAVERSE | SYNCHRONIZE,
FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE,
NULL,
OPEN_EXISTING,
FILE_DIRECTORY_FILE | FILE_SYNCHRONOUS_IO_NONALERT | FILE_FLAG_BACKUP_SEMANTICS,
NULL
);
ptr[3] = saved;
if (hRoot == INVALID_HANDLE_VALUE)
return false;
closeRoot = true;
ptr += 3; // skip "C:\"
}
// UNC path: \\server\share\...
else if (ptr[0] == L'\\' && ptr[1] == L'\\')
{
wchar_t* serverEnd = wcschr(ptr + 2, L'\\');
if (serverEnd == NULL)
return false;
wchar_t* shareEnd = wcschr(serverEnd + 1, L'\\');
if (shareEnd == NULL)
return true; // no parent subfolder exists before file name
wchar_t saved = *shareEnd;
*shareEnd = L'\0';
hRoot = CreateFileW(
fileName,
FILE_LIST_DIRECTORY | FILE_TRAVERSE | SYNCHRONIZE,
FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE,
NULL,
OPEN_EXISTING,
FILE_DIRECTORY_FILE | FILE_SYNCHRONOUS_IO_NONALERT | FILE_FLAG_BACKUP_SEMANTICS,
NULL
);
*shareEnd = saved;
if (hRoot == INVALID_HANDLE_VALUE)
return false;
closeRoot = true;
ptr = shareEnd + 1; // first component under share
}
// Rooted path on current drive: \foo\bar
else if (ptr[0] == L'\\')
{
UNICODE_STRING* dosPath = &ffGetPeb()->ProcessParameters->CurrentDirectory.DosPath;
wchar_t driveRoot[] = { dosPath->Buffer[0], L':', L'\\', L'\0' };
hRoot = CreateFileW(
driveRoot,
FILE_LIST_DIRECTORY | FILE_TRAVERSE | SYNCHRONIZE,
FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE,
NULL,
OPEN_EXISTING,
FILE_DIRECTORY_FILE | FILE_SYNCHRONOUS_IO_NONALERT | FILE_FLAG_BACKUP_SEMANTICS,
NULL
);
if (hRoot == INVALID_HANDLE_VALUE)
return false;
closeRoot = true;
ptr++; // skip leading '\'
}
while (true)
{
wchar_t* token = wcschr(ptr, L'\\');
if (token == NULL)
break;
// Skip empty path segments caused by duplicated '\'
if (token == ptr)
{
ptr = token + 1;
continue;
}
HANDLE hNew = INVALID_HANDLE_VALUE;
IO_STATUS_BLOCK iosb = {};
NTSTATUS status = NtCreateFile(
&hNew,
FILE_LIST_DIRECTORY | FILE_TRAVERSE | SYNCHRONIZE,
&(OBJECT_ATTRIBUTES) {
.Length = sizeof(OBJECT_ATTRIBUTES),
.RootDirectory = hRoot,
.ObjectName = &(UNICODE_STRING) {
.Buffer = ptr,
.Length = (USHORT)((USHORT)(token - ptr) * sizeof(wchar_t)),
.MaximumLength = (USHORT)((USHORT)(token - ptr) * sizeof(wchar_t)),
},
.Attributes = OBJ_CASE_INSENSITIVE,
},
&iosb,
NULL,
FILE_ATTRIBUTE_NORMAL,
FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE,
FILE_OPEN_IF,
FILE_DIRECTORY_FILE | FILE_SYNCHRONOUS_IO_NONALERT,
NULL,
0
);
if (!NT_SUCCESS(status))
{
if (closeRoot && hRoot != INVALID_HANDLE_VALUE)
NtClose(hRoot);
return false;
}
if (closeRoot && hRoot != INVALID_HANDLE_VALUE)
NtClose(hRoot);
hRoot = hNew;
closeRoot = true;
ptr = token + 1;
}
if (closeRoot && hRoot != INVALID_HANDLE_VALUE)
NtClose(hRoot);
return true;
}
bool ffWriteFileData(const char* fileName, size_t dataSize, const void* data)
{
HANDLE FF_AUTO_CLOSE_FD handle = CreateFileA(fileName, GENERIC_WRITE, FILE_SHARE_WRITE, NULL, CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, NULL);
wchar_t fileNameW[MAX_PATH];
ULONG len = 0;
if (!NT_SUCCESS(RtlUTF8ToUnicodeN(fileNameW, (ULONG) sizeof(fileNameW), &len, fileName, (ULONG)strlen(fileName) + 1)))
return false;
for (ULONG i = 0; i < len / sizeof(wchar_t); ++i)
{
if (fileNameW[i] == L'/')
fileNameW[i] = L'\\';
}
HANDLE FF_AUTO_CLOSE_FD handle = CreateFileW(fileNameW, GENERIC_WRITE, FILE_SHARE_WRITE, NULL, CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, NULL);
if (handle == INVALID_HANDLE_VALUE)
{
if (GetLastError() == ERROR_PATH_NOT_FOUND)
{
createSubfolders(fileName);
handle = CreateFileA(fileName, GENERIC_WRITE, FILE_SHARE_WRITE, NULL, CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, NULL);
if (!createSubfolders(fileNameW))
return false;
handle = CreateFileW(fileNameW, GENERIC_WRITE, FILE_SHARE_WRITE, NULL, CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, NULL);
if (handle == INVALID_HANDLE_VALUE)
return false;
}
@@ -70,12 +210,13 @@ static inline void readUntilEOF(HANDLE handle, FFstrbuf* buffer)
bool ffAppendFDBuffer(HANDLE handle, FFstrbuf* buffer)
{
LARGE_INTEGER fileSize;
if(!GetFileSizeEx(handle, &fileSize))
fileSize.QuadPart = 0;
FILE_STANDARD_INFORMATION fileInfo;
IO_STATUS_BLOCK iosb;
if(!NT_SUCCESS(NtQueryInformationFile(handle, &iosb, &fileInfo, sizeof(fileInfo), FileStandardInformation)))
fileInfo.EndOfFile.QuadPart = 0;
if (fileSize.QuadPart > 0)
readWithLength(handle, buffer, (uint32_t)fileSize.QuadPart);
if (fileInfo.EndOfFile.QuadPart > 0)
readWithLength(handle, buffer, (uint32_t)fileInfo.EndOfFile.QuadPart);
else
readUntilEOF(handle, buffer);
@@ -84,24 +225,6 @@ bool ffAppendFDBuffer(HANDLE handle, FFstrbuf* buffer)
return buffer->length > 0;
}
ssize_t ffReadFileData(const char* fileName, size_t dataSize, void* data)
{
HANDLE FF_AUTO_CLOSE_FD handle = CreateFileA(fileName, GENERIC_READ, FILE_SHARE_READ, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL);
if(handle == INVALID_HANDLE_VALUE)
return -1;
return ffReadFDData(handle, dataSize, data);
}
bool ffAppendFileBuffer(const char* fileName, FFstrbuf* buffer)
{
HANDLE FF_AUTO_CLOSE_FD handle = CreateFileA(fileName, GENERIC_READ, FILE_SHARE_READ, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL);
if(handle == INVALID_HANDLE_VALUE)
return false;
return ffAppendFDBuffer(handle, buffer);
}
HANDLE openatW(HANDLE dfd, const wchar_t* fileName, uint16_t fileNameLen, bool directory)
{
assert(fileNameLen <= 0x7FFF);
@@ -128,38 +251,22 @@ HANDLE openatW(HANDLE dfd, const wchar_t* fileName, uint16_t fileNameLen, bool d
return hFile;
}
HANDLE openat(HANDLE dfd, const char* fileName, bool directory)
HANDLE openat(HANDLE dfd, const char* fileName, int oflag)
{
wchar_t fileNameW[MAX_PATH];
int len = MultiByteToWideChar(CP_UTF8, MB_ERR_INVALID_CHARS, fileName, -1, fileNameW, ARRAY_SIZE(fileNameW));
if (len == 0) return INVALID_HANDLE_VALUE;
ULONG len;
if (!NT_SUCCESS(RtlUTF8ToUnicodeN(fileNameW, (ULONG) sizeof(fileNameW), &len, fileName, (ULONG)strlen(fileName) + 1)))
return INVALID_HANDLE_VALUE;
// Implies `fileNameW[len] = L'\0';` and `len` includes the null terminator
len /= sizeof(wchar_t); // convert from bytes to characters
for (int i = 0; i < len - 1; ++i)
for (uint32_t i = 0; i < len - 1; ++i)
{
if (fileNameW[i] == L'/')
fileNameW[i] = L'\\';
}
return openatW(dfd, fileNameW, (uint16_t)(len - 1), directory);
}
bool ffAppendFileBufferRelative(HANDLE dfd, const char* fileName, FFstrbuf* buffer)
{
HANDLE FF_AUTO_CLOSE_FD fd = openat(dfd, fileName, false);
if(fd == INVALID_HANDLE_VALUE)
return false;
return ffAppendFDBuffer(fd, buffer);
}
ssize_t ffReadFileDataRelative(HANDLE dfd, const char* fileName, size_t dataSize, void* data)
{
HANDLE FF_AUTO_CLOSE_FD fd = openat(dfd, fileName, false);
if(fd == INVALID_HANDLE_VALUE)
return -1;
return ffReadFDData(fd, dataSize, data);
return openatW(dfd, fileNameW, (uint16_t)(len - 1), !!(oflag & O_DIRECTORY));
}
bool ffPathExpandEnv(const char* in, FFstrbuf* out)
@@ -172,13 +279,17 @@ bool ffPathExpandEnv(const char* in, FFstrbuf* out)
}
}
DWORD length = ExpandEnvironmentStringsA(in, NULL, 0);
if (length <= 1) return false;
wchar_t pathInW[MAX_PATH], pathOutW[MAX_PATH];
ULONG len = (ULONG) strlen(in);
if (!NT_SUCCESS(RtlUTF8ToUnicodeN(pathInW, (ULONG) sizeof(pathInW), &len, in, len)))
return false;
len /= sizeof(wchar_t); // convert from bytes to characters
ffStrbufClear(out);
ffStrbufEnsureFree(out, (uint32_t)length);
ExpandEnvironmentStringsA(in, out->chars, length);
out->length = (uint32_t)length - 1;
size_t outLen; // in characters, including null terminator
if (!NT_SUCCESS(RtlExpandEnvironmentStrings(NULL, pathInW, len, pathOutW, ARRAY_SIZE(pathOutW), &outLen)))
return false;
ffStrbufSetNWS(out, (uint32_t) outLen - 1, pathOutW);
return true;
}
@@ -308,10 +419,10 @@ const char* ffGetTerminalResponse(const char* request, int nParams, const char*
while (true)
{
if (WaitForSingleObjectEx(hInput, FF_IO_TERM_RESP_WAIT_MS, TRUE) != WAIT_OBJECT_0)
if (NtWaitForSingleObject(hInput, FALSE, &(LARGE_INTEGER) { .QuadPart = (int64_t) FF_IO_TERM_RESP_WAIT_MS * -10000 }) != STATUS_WAIT_0)
{
SetConsoleMode(hInput, inputMode);
return "WaitForSingleObject() failed or timeout";
return "NtWaitForSingleObject() failed or timeout";
}
// Ignore all unexpected input events
@@ -350,7 +461,7 @@ const char* ffGetTerminalResponse(const char* request, int nParams, const char*
va_list cargs;
va_copy(cargs, args);
int ret = vsscanf(buffer, format, args);
int ret = vsscanf(buffer, format, cargs);
va_end(cargs);
if (ret <= 0)
-95
View File
@@ -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 <sys/param.h>
#include <sys/module.h>
bool ffKmodLoaded(const char* modName)
{
return modfind(modName) >= 0;
}
#elif __NetBSD__
#include "common/stringUtils.h"
#include <sys/module.h>
#include <sys/param.h>
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 <IOKit/kext/KextManager.h>
#include <CoreFoundation/CoreFoundation.h>
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
+13
View File
@@ -0,0 +1,13 @@
#include "common/kmod.h"
#include "common/apple/cf_helpers.h"
#include <IOKit/kext/KextManager.h>
#include <CoreFoundation/CoreFoundation.h>
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);
}
+8
View File
@@ -0,0 +1,8 @@
#include "common/kmod.h"
#include <sys/param.h>
#include <sys/module.h>
bool ffKmodLoaded(const char* modName)
{
return modfind(modName) >= 0;
}
+23
View File
@@ -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;
}
+43
View File
@@ -0,0 +1,43 @@
#include "common/kmod.h"
#include "common/stringUtils.h"
#include <sys/module.h>
#include <sys/param.h>
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;
}
+6
View File
@@ -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
}
+24
View File
@@ -0,0 +1,24 @@
#include "common/kmod.h"
#include "common/stringUtils.h"
#include <sys/modctl.h>
#include <errno.h>
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);
}
+27
View File
@@ -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;
}
+86 -1
View File
@@ -1,6 +1,13 @@
#include "fastfetch.h"
#include "common/library.h"
#if _WIN32
#include "common/debug.h"
#include "common/windows/nt.h"
#include <errno.h>
#include <ntstatus.h>
#endif
#ifndef FF_DISABLE_DLOPEN
#include <stdarg.h>
@@ -36,7 +43,7 @@ static void* libraryLoad(const char* path, int maxVersion)
if (pathLen == instance.state.platform.exePath.length)
return result;
char absPath[MAX_PATH + 1];
char absPath[MAX_PATH * 2];
strcpy(mempcpy(absPath, instance.state.platform.exePath.chars, pathLen + 1), path);
return dlopen(absPath, FF_DLOPEN_FLAGS);
@@ -92,3 +99,81 @@ void* ffLibraryLoad(const char* path, int maxVersion, ...)
}
#endif
#if _WIN32
void* dlopen(const char* path, FF_MAYBE_UNUSED int mode)
{
wchar_t pathW[MAX_PATH + 1];
ULONG pathWBytes = 0;
NTSTATUS status = RtlUTF8ToUnicodeN(pathW, sizeof(pathW), &pathWBytes, path, (uint32_t)strlen(path) + 1);
if (!NT_SUCCESS(status))
{
FF_DEBUG("RtlUTF8ToUnicodeN failed for path %s with status 0x%08lX: %s", path, status, ffDebugNtStatus(status));
return NULL;
}
PVOID module = NULL;
status = LdrLoadDll(NULL, NULL, &(UNICODE_STRING) {
.Length = (USHORT) pathWBytes - sizeof(wchar_t), // Exclude null terminator
.MaximumLength = (USHORT) pathWBytes,
.Buffer = pathW,
}, &module);
if (!NT_SUCCESS(status))
{
FF_DEBUG("LdrLoadDll failed for path %s with status 0x%08lX: %s", path, status, ffDebugNtStatus(status));
return NULL;
}
return module;
}
int dlclose(void* handle)
{
NTSTATUS status = LdrUnloadDll(handle);
if (!NT_SUCCESS(status))
{
FF_DEBUG("LdrUnloadDll failed for handle %p with status 0x%08lX: %s", handle, status, ffDebugNtStatus(status));
return -1;
}
return 0;
}
void* dlsym(void* handle, const char* symbol)
{
void* address;
USHORT symbolBytes = (USHORT) strlen(symbol) + 1;
NTSTATUS status = LdrGetProcedureAddress(handle, &(ANSI_STRING) {
.Length = symbolBytes - sizeof(char),
.MaximumLength = symbolBytes,
.Buffer = (char*) symbol,
}, 0, &address);
if (!NT_SUCCESS(status))
{
FF_DEBUG("LdrGetProcedureAddress failed for symbol %s with status 0x%08lX: %s", symbol, status, ffDebugNtStatus(status));
return NULL;
}
return address;
}
void* ffLibraryGetModule(const wchar_t* libraryFileName)
{
assert(libraryFileName != NULL && "Use \"ffGetPeb()->ImageBaseAddress\" instead");
void* module = NULL;
USHORT libraryFileNameBytes = (USHORT) (wcslen(libraryFileName) * sizeof(wchar_t)) + sizeof(wchar_t);
NTSTATUS status = LdrGetDllHandle(NULL, NULL, &(UNICODE_STRING) {
.Length = libraryFileNameBytes - sizeof(wchar_t),
.MaximumLength = libraryFileNameBytes,
.Buffer = (wchar_t*) libraryFileName,
}, &module);
if (!NT_SUCCESS(status))
{
FF_DEBUG("LdrGetDllHandle failed for library %ls with status 0x%08lX: %s", libraryFileName, status, ffDebugNtStatus(status));
return NULL;
}
return module;
}
#endif
+19
View File
@@ -0,0 +1,19 @@
#include "common/memrchr.h"
#include <stddef.h>
#include <stdint.h>
void* memrchr(const void* s, int c, size_t n)
{
if (n == 0) return NULL;
const uint8_t uc = (uint8_t) c;
const uint8_t* p = (const uint8_t*) s + n;
while (n--)
{
if (*--p == uc) return (void*) p;
}
return NULL;
}
+6 -6
View File
@@ -72,7 +72,7 @@ bool ffNetifGetDefaultRouteImplV4(FFNetifDefaultRouteResult* result)
setsockopt(pfRoute, SOL_SOCKET, SO_SNDTIMEO, (char *)&timeout, sizeof(timeout));
}
int pid = getpid();
uint32_t pid = instance.state.platform.pid;
struct {
struct rt_msghdr hdr;
@@ -85,7 +85,7 @@ bool ffNetifGetDefaultRouteImplV4(FFNetifDefaultRouteResult* result)
.rtm_version = RTM_VERSION,
.rtm_addrs = RTA_DST | RTA_IFP | RTA_IFA,
.rtm_msglen = sizeof(rtmsg.hdr) + sizeof(rtmsg.dst),
.rtm_pid = pid,
.rtm_pid = (pid_t) pid,
.rtm_seq = 1,
},
.dst = {
@@ -99,7 +99,7 @@ bool ffNetifGetDefaultRouteImplV4(FFNetifDefaultRouteResult* result)
if (send(pfRoute, &rtmsg, rtmsg.hdr.rtm_msglen, 0) != rtmsg.hdr.rtm_msglen)
return false;
while (recv(pfRoute, &rtmsg, sizeof(rtmsg), 0) > 0 && !(rtmsg.hdr.rtm_seq == 1 && rtmsg.hdr.rtm_pid == pid))
while (recv(pfRoute, &rtmsg, sizeof(rtmsg), 0) > 0 && !(rtmsg.hdr.rtm_seq == 1 && rtmsg.hdr.rtm_pid == (pid_t) pid))
;
#ifndef __sun // On Solaris, the RTF_GATEWAY flag is not set for default routes for some reason
@@ -145,7 +145,7 @@ bool ffNetifGetDefaultRouteImplV6(FFNetifDefaultRouteResult* result)
setsockopt(pfRoute, SOL_SOCKET, SO_SNDTIMEO, (char *)&timeout, sizeof(timeout));
}
int pid = getpid();
uint32_t pid = instance.state.platform.pid;
struct {
struct rt_msghdr hdr;
@@ -158,7 +158,7 @@ bool ffNetifGetDefaultRouteImplV6(FFNetifDefaultRouteResult* result)
.rtm_version = RTM_VERSION,
.rtm_addrs = RTA_DST | RTA_IFP,
.rtm_msglen = sizeof(rtmsg.hdr) + sizeof(rtmsg.dst),
.rtm_pid = pid,
.rtm_pid = (pid_t) pid,
.rtm_seq = 2,
},
.dst = {
@@ -172,7 +172,7 @@ bool ffNetifGetDefaultRouteImplV6(FFNetifDefaultRouteResult* result)
if (send(pfRoute, &rtmsg, rtmsg.hdr.rtm_msglen, 0) != rtmsg.hdr.rtm_msglen)
return false;
while (recv(pfRoute, &rtmsg, sizeof(rtmsg), 0) > 0 && !(rtmsg.hdr.rtm_seq == 2 && rtmsg.hdr.rtm_pid == pid))
while (recv(pfRoute, &rtmsg, sizeof(rtmsg), 0) > 0 && !(rtmsg.hdr.rtm_seq == 2 && rtmsg.hdr.rtm_pid == (pid_t) pid))
;
#ifndef __sun // On Solaris, the RTF_GATEWAY flag is not set for default routes for some reason
+2 -2
View File
@@ -19,7 +19,7 @@ bool ffNetifGetDefaultRouteImplV4(FFNetifDefaultRouteResult* result)
}
FF_DEBUG("Created netlink socket: fd=%d", sock_fd);
unsigned pid = (unsigned) getpid();
uint32_t pid = instance.state.platform.pid;
FF_DEBUG("Process PID: %u", pid);
// Bind socket
@@ -235,7 +235,7 @@ bool ffNetifGetDefaultRouteImplV6(FFNetifDefaultRouteResult* result)
}
FF_DEBUG("Created netlink socket: fd=%d", sock_fd);
unsigned pid = (unsigned) getpid();
uint32_t pid = instance.state.platform.pid;
FF_DEBUG("Process PID: %u", pid);
// Bind socket
+13 -8
View File
@@ -108,8 +108,7 @@ static const char* tryNonThreadingFastPath(FFNetworkingState* state)
#else
"sendto()"
#endif
" %s (sent=%zd, errno=%d: %s)", errno == 0 ? "succeeded" : "was in progress",
sent, errno, strerror(errno));
" %s (sent=%zd, %s)", errno == 0 ? "succeeded" : "was in progress", sent, strerror(errno));
freeaddrinfo(state->addr);
state->addr = NULL;
ffStrbufDestroy(&state->command);
@@ -122,7 +121,7 @@ static const char* tryNonThreadingFastPath(FFNetworkingState* state)
#else
"sendto()"
#endif
" failed: %s (errno=%d)", strerror(errno), errno);
" failed: %s", strerror(errno));
#ifdef __APPLE__
return "connectx() failed";
#else
@@ -143,7 +142,7 @@ static const char* connectAndSend(FFNetworkingState* state)
FF_DEBUG("Attempting connect() to server...");
if(connect(state->sockfd, state->addr->ai_addr, state->addr->ai_addrlen) == -1)
{
FF_DEBUG("connect() failed: %s (errno=%d)", strerror(errno), errno);
FF_DEBUG("connect() failed: %s", strerror(errno));
ret = "connect() failed";
goto error;
}
@@ -152,7 +151,7 @@ static const char* connectAndSend(FFNetworkingState* state)
FF_DEBUG("Attempting to send %u bytes of data...", state->command.length);
if(send(state->sockfd, state->command.chars, state->command.length, 0) < 0)
{
FF_DEBUG("send() failed: %s (errno=%d)", strerror(errno), errno);
FF_DEBUG("send() failed: %s", strerror(errno));
ret = "send() failed";
goto error;
}
@@ -227,7 +226,7 @@ static const char* initNetworkingState(FFNetworkingState* state, const char* hos
state->sockfd = socket(state->addr->ai_family, state->addr->ai_socktype, state->addr->ai_protocol);
if(state->sockfd == -1)
{
FF_DEBUG("socket() failed: %s (errno=%d)", strerror(errno), errno);
FF_DEBUG("socket() failed: %s", strerror(errno));
ret = "socket() failed";
goto error;
}
@@ -399,7 +398,7 @@ const char* ffNetworkingRecvHttpResponse(FFNetworkingState* state, FFstrbuf* buf
}
else if (pollRes == -1)
{
FF_DEBUG("poll() failed: %s (errno=%d)", strerror(errno), errno);
FF_DEBUG("poll() failed: %s", strerror(errno));
close(state->sockfd);
state->sockfd = -1;
return "poll() failed";
@@ -417,6 +416,12 @@ const char* ffNetworkingRecvHttpResponse(FFNetworkingState* state, FFstrbuf* buf
}
#endif
if (shutdown(state->sockfd, SHUT_WR) == -1)
{
FF_DEBUG("Failed to shutdown socket send: %s", strerror(errno));
// Not a critical error, continue anyway
}
FF_DEBUG("Starting data reception");
FF_MAYBE_UNUSED int recvCount = 0;
uint32_t contentLength = 0;
@@ -434,7 +439,7 @@ const char* ffNetworkingRecvHttpResponse(FFNetworkingState* state, FFstrbuf* buf
if (received == 0) {
FF_DEBUG("Connection closed (received=0)");
} else {
FF_DEBUG("Reception failed: %s (errno=%d)", strerror(errno), errno);
FF_DEBUG("Reception failed: %s", strerror(errno));
}
break;
}
+43 -25
View File
@@ -24,19 +24,19 @@ static const char* initWsaData(WSADATA* wsaData)
}
//Dummy socket needed for WSAIoctl
SOCKET sockfd = socket(AF_INET, SOCK_STREAM, 0);
SOCKET sockfd = WSASocketW(AF_INET, SOCK_STREAM, 0, NULL, 0, 0);
if(sockfd == INVALID_SOCKET) {
FF_DEBUG("socket(AF_INET, SOCK_STREAM) failed");
FF_DEBUG("WSASocketW(AF_INET, SOCK_STREAM) failed");
WSACleanup();
return "socket(AF_INET, SOCK_STREAM) failed";
return "WSASocketW(AF_INET, SOCK_STREAM) failed";
}
DWORD dwBytes;
GUID guid = WSAID_CONNECTEX;
if(WSAIoctl(sockfd, SIO_GET_EXTENSION_FUNCTION_POINTER,
&guid, sizeof(guid),
&ConnectEx, sizeof(ConnectEx),
&dwBytes, NULL, NULL) != 0) {
&guid, sizeof(guid),
&ConnectEx, sizeof(ConnectEx),
&dwBytes, NULL, NULL) != 0) {
FF_DEBUG("WSAIoctl(sockfd, SIO_GET_EXTENSION_FUNCTION_POINTER) failed");
closesocket(sockfd);
WSACleanup();
@@ -92,26 +92,33 @@ const char* ffNetworkingSendHttpRequest(FFNetworkingState* state, const char* ho
return "initWsaData() failed before";
}
struct addrinfo* addr;
struct addrinfo hints = {
ADDRINFOW* addr;
ADDRINFOW hints = {
.ai_flags = AI_NUMERICSERV,
.ai_family = state->ipv6 ? AF_INET6 : AF_INET,
.ai_socktype = SOCK_STREAM,
.ai_flags = AI_NUMERICSERV,
};
FF_DEBUG("Resolving address: %s (%s)", host, state->ipv6 ? "IPv6" : "IPv4");
if(getaddrinfo(host, "80", &hints, &addr) != 0)
wchar_t hostW[256];
if (!NT_SUCCESS(RtlUTF8ToUnicodeN(hostW, (ULONG) sizeof(hostW), NULL, host, (ULONG) strlen(host) + 1)))
{
FF_DEBUG("getaddrinfo() failed");
return "getaddrinfo() failed";
FF_DEBUG("Failed to convert host to wide string: %s", host);
return "Failed to convert host to wide string";
}
state->sockfd = socket(addr->ai_family, addr->ai_socktype, addr->ai_protocol);
FF_DEBUG("Resolving address: %s (%s)", host, state->ipv6 ? "IPv6" : "IPv4");
if(GetAddrInfoW(hostW, L"80", &hints, &addr) != 0)
{
FF_DEBUG("GetAddrInfoW() failed");
return "GetAddrInfoW() failed";
}
state->sockfd = WSASocketW(addr->ai_family, addr->ai_socktype, addr->ai_protocol, NULL, 0, 0);
if(state->sockfd == INVALID_SOCKET)
{
FF_DEBUG("socket() failed");
freeaddrinfo(addr);
return "socket() failed";
FF_DEBUG("WSASocketW() failed");
FreeAddrInfoW(addr);
return "WSASocketW() failed";
}
DWORD flag = 1;
@@ -143,7 +150,7 @@ const char* ffNetworkingSendHttpRequest(FFNetworkingState* state, const char* ho
{
FF_DEBUG("bind() failed: %s", ffDebugWin32Error((DWORD) WSAGetLastError()));
closesocket(state->sockfd);
freeaddrinfo(addr);
FreeAddrInfoW(addr);
state->sockfd = INVALID_SOCKET;
return "bind() failed";
}
@@ -156,7 +163,7 @@ const char* ffNetworkingSendHttpRequest(FFNetworkingState* state, const char* ho
if (state->overlapped.hEvent == WSA_INVALID_EVENT) {
FF_DEBUG("WSACreateEvent() failed");
closesocket(state->sockfd);
freeaddrinfo(addr);
FreeAddrInfoW(addr);
state->sockfd = INVALID_SOCKET;
return "WSACreateEvent() failed";
}
@@ -200,7 +207,7 @@ const char* ffNetworkingSendHttpRequest(FFNetworkingState* state, const char* ho
BOOL result = ConnectEx(state->sockfd, addr->ai_addr, (int)addr->ai_addrlen,
state->command.chars, state->command.length, &sent, &state->overlapped);
freeaddrinfo(addr);
FreeAddrInfoW(addr);
addr = NULL;
if(!result)
@@ -251,7 +258,8 @@ const char* ffNetworkingRecvHttpResponse(FFNetworkingState* state, FFstrbuf* buf
} else {
FF_DEBUG("WSAWaitForMultipleEvents failed: %s", ffDebugWin32Error((DWORD) WSAGetLastError()));
}
CancelIo((HANDLE) state->sockfd);
if (CancelIoEx((HANDLE) state->sockfd, &state->overlapped))
WSAWaitForMultipleEvents(1, &state->overlapped.hEvent, TRUE, 10, TRUE);
WSACloseEvent(state->overlapped.hEvent);
closesocket(state->sockfd);
ffStrbufDestroy(&state->command);
@@ -279,6 +287,12 @@ const char* ffNetworkingRecvHttpResponse(FFNetworkingState* state, FFstrbuf* buf
// Not a critical error, continue anyway
}
if (shutdown(state->sockfd, SD_SEND) == SOCKET_ERROR)
{
FF_DEBUG("Failed to shutdown socket send: %s", ffDebugWin32Error((DWORD) WSAGetLastError()));
// Not a critical error, continue anyway
}
if(timeout > 0)
{
FF_DEBUG("Setting receive timeout: %u ms", timeout);
@@ -302,10 +316,14 @@ const char* ffNetworkingRecvHttpResponse(FFNetworkingState* state, FFstrbuf* buf
FF_DEBUG("Data reception loop #%d, current buffer size: %u, available space: %u",
++recvCount, buffer->length, ffStrbufGetFree(buffer));
ssize_t received = recv(state->sockfd, buffer->chars + buffer->length, (int)ffStrbufGetFree(buffer), 0);
DWORD received = 0, recvFlags = 0;
int recvResult = WSARecv(state->sockfd, &(WSABUF) {
.buf = buffer->chars + buffer->length,
.len = (ULONG) ffStrbufGetFree(buffer),
}, 1, &received, &recvFlags, NULL, NULL);
if (received <= 0) {
if (received == 0) {
if (recvResult == SOCKET_ERROR || received == 0) {
if (recvResult == 0 && received == 0) {
FF_DEBUG("Connection closed (received=0)");
} else {
FF_DEBUG("Reception failed: %s", ffDebugWin32Error((DWORD) WSAGetLastError()));
@@ -316,7 +334,7 @@ const char* ffNetworkingRecvHttpResponse(FFNetworkingState* state, FFstrbuf* buf
buffer->length += (uint32_t) received;
buffer->chars[buffer->length] = '\0';
FF_DEBUG("Successfully received %zd bytes of data, total: %u bytes", received, buffer->length);
FF_DEBUG("Successfully received %u bytes of data, total: %u bytes", (unsigned) received, buffer->length);
// Check if HTTP header end marker is found
if (headerEnd == 0) {
+260 -1
View File
@@ -1,6 +1,6 @@
#include "common/path.h"
#include "common/io.h"
#include "common/stringUtils.h"
#include "common/arrayUtils.h"
#if !_WIN32
const char* ffFindExecutableInPath(const char* name, FFstrbuf* result)
@@ -49,6 +49,9 @@ const char* ffFindExecutableInPath(const char* name, FFstrbuf* result)
}
#else
#include <windows.h>
#include <winioctl.h>
#include <errno.h>
#include <stdalign.h>
const char* ffFindExecutableInPath(const char* name, FFstrbuf* result)
{
@@ -62,4 +65,260 @@ const char* ffFindExecutableInPath(const char* name, FFstrbuf* result)
ffStrbufSetS(result, buffer);
return NULL;
}
static inline int winerr2Errno(DWORD err)
{
switch (err)
{
case ERROR_FILE_NOT_FOUND:
case ERROR_PATH_NOT_FOUND:
case ERROR_INVALID_NAME:
return ENOENT;
case ERROR_ACCESS_DENIED:
case ERROR_SHARING_VIOLATION:
case ERROR_LOCK_VIOLATION:
return EACCES;
case ERROR_BUFFER_OVERFLOW:
case ERROR_INSUFFICIENT_BUFFER:
return ENAMETOOLONG;
case ERROR_INVALID_PARAMETER:
case ERROR_NOT_A_REPARSE_POINT:
return EINVAL;
default:
return EIO;
}
}
char* frealpath(HANDLE hFile, char* resolved_name)
{
if (__builtin_expect(hFile == INVALID_HANDLE_VALUE || !hFile, false))
{
errno = EINVAL;
return NULL;
}
wchar_t resolvedNameW[MAX_PATH + 4]; /* +4 for "\\\\?\\" prefix */
DWORD lenW = GetFinalPathNameByHandleW(hFile, resolvedNameW, (DWORD)ARRAY_SIZE(resolvedNameW), FILE_NAME_NORMALIZED);
if (lenW == 0)
{
errno = winerr2Errno(GetLastError());
return NULL;
}
if (lenW >= ARRAY_SIZE(resolvedNameW))
{
errno = E2BIG;
return NULL;
}
lenW++; // Include null terminator
wchar_t* srcW = resolvedNameW;
DWORD srcLenW = lenW;
if (srcLenW >= 8 && wcsncmp(resolvedNameW, L"\\\\?\\UNC\\", 8) == 0)
{
/* Convert "\\?\UNC\server\share" to "\\server\share" */
srcW += 6;
srcLenW -= 6;
*srcW = L'\\';
}
else if (srcLenW >= 4 && wcsncmp(resolvedNameW, L"\\\\?\\", 4) == 0)
{
srcW += 4;
srcLenW -= 4;
}
if (resolved_name)
{
ULONG outBytes = 0;
if (!NT_SUCCESS(RtlUnicodeToUTF8N(resolved_name, MAX_PATH, &outBytes, srcW, (ULONG)(srcLenW * sizeof(wchar_t)))))
{
errno = E2BIG;
return NULL;
}
if (outBytes > MAX_PATH)
{
errno = E2BIG;
return NULL;
}
return resolved_name;
}
else
{
/* UTF-8 worst-case: up to 4 bytes per UTF-16 code unit */
char tmp[(MAX_PATH + 4) * 4];
ULONG outBytes = 0;
if (!NT_SUCCESS(RtlUnicodeToUTF8N(tmp, (ULONG)sizeof(tmp), &outBytes, srcW, (ULONG)(srcLenW * sizeof(wchar_t)))))
{
errno = E2BIG;
return NULL;
}
resolved_name = (char*)malloc(outBytes);
if (!resolved_name)
{
errno = ENOMEM;
return NULL;
}
memcpy(resolved_name, tmp, outBytes);
return resolved_name;
}
return resolved_name;
}
char* realpath(const char* __restrict file_name, char* __restrict resolved_name)
{
if (!file_name)
{
errno = EINVAL;
return NULL;
}
wchar_t fileNameW[MAX_PATH];
ULONG lenBytes = 0;
if (!NT_SUCCESS(RtlUTF8ToUnicodeN(fileNameW, (ULONG)sizeof(fileNameW), &lenBytes, file_name, (ULONG)strlen(file_name) + 1)))
{
errno = EINVAL;
return NULL;
}
FF_AUTO_CLOSE_FD HANDLE hFile = CreateFileW(
fileNameW,
0,
FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE,
NULL,
OPEN_EXISTING,
FILE_FLAG_BACKUP_SEMANTICS,
NULL);
if (hFile == INVALID_HANDLE_VALUE)
{
errno = winerr2Errno(GetLastError());
return NULL;
}
return frealpath(hFile, resolved_name);
}
ssize_t freadlink(HANDLE hFile, char* buf, size_t bufsiz)
{
if (__builtin_expect(hFile == INVALID_HANDLE_VALUE || !buf || bufsiz == 0, false))
{
errno = EINVAL;
return -1;
}
alignas(REPARSE_DATA_BUFFER) BYTE reparseBuf[MAXIMUM_REPARSE_DATA_BUFFER_SIZE];
DWORD bytesReturned = 0;
if (!DeviceIoControl(hFile, FSCTL_GET_REPARSE_POINT, NULL, 0, reparseBuf, (DWORD) sizeof(reparseBuf), &bytesReturned, NULL))
{
errno = winerr2Errno(GetLastError());
return -1;
}
REPARSE_DATA_BUFFER* rp = (REPARSE_DATA_BUFFER*) reparseBuf;
const wchar_t* targetW = NULL;
USHORT targetBytes = 0;
if (rp->ReparseTag == IO_REPARSE_TAG_SYMLINK)
{
if (rp->SymbolicLinkReparseBuffer.PrintNameLength > 0)
{
targetW = rp->SymbolicLinkReparseBuffer.PathBuffer +
(rp->SymbolicLinkReparseBuffer.PrintNameOffset / sizeof(wchar_t));
targetBytes = rp->SymbolicLinkReparseBuffer.PrintNameLength;
}
else
{
targetW = rp->SymbolicLinkReparseBuffer.PathBuffer +
(rp->SymbolicLinkReparseBuffer.SubstituteNameOffset / sizeof(wchar_t));
targetBytes = rp->SymbolicLinkReparseBuffer.SubstituteNameLength;
if (targetBytes >= 8 &&
wcsncmp(targetW, L"\\??\\", 4) == 0)
{
targetW += 4;
targetBytes -= 8;
}
}
}
else if (rp->ReparseTag == IO_REPARSE_TAG_MOUNT_POINT)
{
if (rp->MountPointReparseBuffer.PrintNameLength > 0)
{
targetW = rp->MountPointReparseBuffer.PathBuffer +
(rp->MountPointReparseBuffer.PrintNameOffset / sizeof(wchar_t));
targetBytes = rp->MountPointReparseBuffer.PrintNameLength;
}
else
{
targetW = rp->MountPointReparseBuffer.PathBuffer +
(rp->MountPointReparseBuffer.SubstituteNameOffset / sizeof(wchar_t));
targetBytes = rp->MountPointReparseBuffer.SubstituteNameLength;
if (targetBytes >= 8 &&
wcsncmp(targetW, L"\\??\\", 4) == 0)
{
targetW += 4;
targetBytes -= 8;
}
}
}
else
{
errno = EINVAL;
return -1;
}
ULONG outBytes = 0;
if (!NT_SUCCESS(RtlUnicodeToUTF8N(buf, (ULONG) bufsiz, &outBytes, targetW, targetBytes)))
{
errno = E2BIG;
return -1;
}
// Not null-terminated
return (ssize_t) outBytes;
}
ssize_t readlink(const char* path, char* buf, size_t bufsiz)
{
if (!path || !buf || bufsiz == 0)
{
errno = EINVAL;
return -1;
}
wchar_t pathW[MAX_PATH];
ULONG pathWBytes = 0;
if (!NT_SUCCESS(RtlUTF8ToUnicodeN(pathW, (ULONG) sizeof(pathW), &pathWBytes, path, (ULONG) strlen(path) + 1)))
{
errno = EINVAL;
return -1;
}
FF_AUTO_CLOSE_FD HANDLE hFile = CreateFileW(
pathW,
0,
FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE,
NULL,
OPEN_EXISTING,
FILE_FLAG_BACKUP_SEMANTICS | FILE_FLAG_OPEN_REPARSE_POINT,
NULL
);
if (hFile == INVALID_HANDLE_VALUE)
{
errno = winerr2Errno(GetLastError());
return -1;
}
return freadlink(hFile, buf, bufsiz);
}
#endif
+2
View File
@@ -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;
+2 -2
View File
@@ -47,8 +47,8 @@ void ffPrintLogoAndKey(const char* moduleName, uint8_t moduleIndex, const FFModu
{
FF_STRBUF_AUTO_DESTROY key = ffStrbufCreate();
FF_PARSE_FORMAT_STRING_CHECKED(&key, &moduleArgs->key, ((FFformatarg[]) {
FF_FORMAT_ARG(moduleIndex, "index"),
FF_FORMAT_ARG(moduleArgs->keyIcon, "icon"),
FF_ARG(moduleIndex, "index"),
FF_ARG(moduleArgs->keyIcon, "icon"),
}));
ffStrbufWriteTo(&key, stdout);
}
+2
View File
@@ -128,6 +128,8 @@ const char* ffProcessSpawn(char* const argv[], bool useStdErr, FFProcessHandle*
{
close(pipes[0]);
close(pipes[1]);
if (ret == ENOENT)
return "command not found";
return "posix_spawnp() failed";
}
+114 -66
View File
@@ -1,10 +1,13 @@
#include "fastfetch.h"
#include "common/mallocHelper.h"
#include "common/processing.h"
#include "common/io.h"
#include "common/windows/unicode.h"
#include "common/windows/nt.h"
#include <stdalign.h>
#include <windows.h>
#include <ntstatus.h>
#include <winternl.h>
enum { FF_PIPE_BUFSIZ = 8192 };
@@ -51,7 +54,7 @@ const char* ffProcessSpawn(char* const argv[], bool useStdErr, FFProcessHandle*
wchar_t pipeName[32];
static unsigned pidCounter = 0;
swprintf(pipeName, ARRAY_SIZE(pipeName), L"\\\\.\\pipe\\FASTFETCH-%u-%u", GetCurrentProcessId(), ++pidCounter);
swprintf(pipeName, ARRAY_SIZE(pipeName), L"\\\\.\\pipe\\FASTFETCH-%u-%u", instance.state.platform.pid, ++pidCounter);
FF_AUTO_CLOSE_FD HANDLE hChildPipeRead = CreateNamedPipeW(
pipeName,
@@ -83,7 +86,7 @@ const char* ffProcessSpawn(char* const argv[], bool useStdErr, FFProcessHandle*
return "CreateFileW(L\"\\\\.\\pipe\\FASTFETCH-$(PID)\") failed";
PROCESS_INFORMATION piProcInfo = {};
STARTUPINFOA siStartInfo = {
STARTUPINFOW siStartInfo = {
.cb = sizeof(siStartInfo),
.dwFlags = STARTF_USESTDHANDLES,
};
@@ -98,12 +101,19 @@ const char* ffProcessSpawn(char* const argv[], bool useStdErr, FFProcessHandle*
siStartInfo.hStdError = ffGetNullFD();
}
FF_STRBUF_AUTO_DESTROY cmdline = ffStrbufCreate();
argvToCmdline(argv, &cmdline);
FF_AUTO_FREE wchar_t* cmdline = NULL;
{
FF_STRBUF_AUTO_DESTROY buf = ffStrbufCreate();
argvToCmdline(argv, &buf);
uint32_t cmdlineBytes = (buf.length + 1) * sizeof(wchar_t);
cmdline = malloc(cmdlineBytes);
if (!NT_SUCCESS(RtlUTF8ToUnicodeN(cmdline, cmdlineBytes, NULL, buf.chars, buf.length + 1)))
return "RtlUTF8ToUnicodeN() failed";
}
BOOL success = CreateProcessA(
BOOL success = CreateProcessW(
NULL, // application name
cmdline.chars, // command line
cmdline, // command line
NULL, // process security attributes
NULL, // primary thread security attributes
TRUE, // handles are inherited
@@ -114,15 +124,15 @@ const char* ffProcessSpawn(char* const argv[], bool useStdErr, FFProcessHandle*
&piProcInfo // receives PROCESS_INFORMATION
);
CloseHandle(hChildPipeWrite);
NtClose(hChildPipeWrite);
if(!success)
{
if (GetLastError() == ERROR_FILE_NOT_FOUND)
return "command not found";
return "CreateProcessA() failed";
return "CreateProcessW() failed";
}
CloseHandle(piProcInfo.hThread); // we don't need the thread handle
NtClose(piProcInfo.hThread); // we don't need the thread handle
outHandle->pid = piProcInfo.hProcess;
outHandle->pipeRead = hChildPipeRead;
hChildPipeRead = INVALID_HANDLE_VALUE; // ownership transferred, don't close it
@@ -130,6 +140,17 @@ const char* ffProcessSpawn(char* const argv[], bool useStdErr, FFProcessHandle*
return NULL;
}
static void terminateChildProcess(HANDLE hProcess, HANDLE hChildPipeRead, HANDLE hReadEvent, IO_STATUS_BLOCK* piosb)
{
IO_STATUS_BLOCK cancelIosb = {};
if (NT_SUCCESS(NtCancelIoFileEx(hChildPipeRead, piosb, &cancelIosb)))
{
if (hReadEvent)
NtWaitForSingleObject(hReadEvent, FALSE, &(LARGE_INTEGER) { .QuadPart = -100000 }); // wait for cancellation to complete
}
NtTerminateProcess(hProcess, 1);
}
const char* ffProcessReadOutput(FFProcessHandle* handle, FFstrbuf* buffer)
{
assert(handle->pipeRead != INVALID_HANDLE_VALUE);
@@ -138,64 +159,74 @@ const char* ffProcessReadOutput(FFProcessHandle* handle, FFstrbuf* buffer)
int32_t timeout = instance.config.general.processingTimeout;
FF_AUTO_CLOSE_FD HANDLE hProcess = handle->pid;
FF_AUTO_CLOSE_FD HANDLE hChildPipeRead = handle->pipeRead;
FF_AUTO_CLOSE_FD HANDLE hReadEvent = NULL;
handle->pid = INVALID_HANDLE_VALUE;
handle->pipeRead = INVALID_HANDLE_VALUE;
if (timeout >= 0 && !NT_SUCCESS(NtCreateEvent(&hReadEvent, EVENT_ALL_ACCESS, NULL, SynchronizationEvent, FALSE)))
return "NtCreateEvent() failed";
char str[FF_PIPE_BUFSIZ];
DWORD nRead = 0;
OVERLAPPED overlapped = {};
// ReadFile always completes synchronously if the pipe is not created with FILE_FLAG_OVERLAPPED
uint32_t nRead = 0;
IO_STATUS_BLOCK iosb = {};
do
{
if (!ReadFile(hChildPipeRead, str, sizeof(str), &nRead, &overlapped))
NTSTATUS status = NtReadFile(
hChildPipeRead,
hReadEvent,
NULL,
NULL,
&iosb,
str,
(ULONG) sizeof(str),
NULL,
NULL
);
if (status == STATUS_PENDING)
{
switch (GetLastError())
switch (NtWaitForSingleObject(hReadEvent, FALSE, &(LARGE_INTEGER) { .QuadPart = (int64_t) timeout * -10000 }))
{
case ERROR_IO_PENDING:
#if !FF_WIN7_COMPAT
if (!GetOverlappedResultEx(hChildPipeRead, &overlapped, &nRead, timeout < 0 ? INFINITE : (DWORD) timeout, FALSE))
#else
// To support Windows 7
if (timeout >= 0 && WaitForSingleObject(hChildPipeRead, (DWORD) timeout) != WAIT_OBJECT_0)
{
CancelIo(hChildPipeRead);
TerminateProcess(hProcess, 1);
return "WaitForSingleObject(hChildPipeRead) failed or timeout (try increasing --processing-timeout)";
}
if (!GetOverlappedResult(hChildPipeRead, &overlapped, &nRead, FALSE))
#endif
{
if (GetLastError() == ERROR_BROKEN_PIPE)
return NULL;
CancelIo(hChildPipeRead);
TerminateProcess(hProcess, 1);
return "GetOverlappedResult"
#if !FF_WIN7_COMPAT
"Ex"
#endif
"(hChildPipeRead) failed";
}
case STATUS_WAIT_0:
status = iosb.Status;
break;
case ERROR_BROKEN_PIPE:
goto exit;
case STATUS_TIMEOUT:
{
terminateChildProcess(hProcess, hChildPipeRead, hReadEvent, &iosb);
return "NtReadFile(hChildPipeRead) timed out";
}
default:
CancelIo(hChildPipeRead);
TerminateProcess(hProcess, 1);
return "ReadFile(hChildPipeRead) failed";
terminateChildProcess(hProcess, hChildPipeRead, hReadEvent, &iosb);
return "NtWaitForSingleObject(hReadEvent) failed";
}
}
if (status == STATUS_PIPE_BROKEN || status == STATUS_END_OF_FILE)
goto exit;
if (!NT_SUCCESS(status))
{
terminateChildProcess(hProcess, hChildPipeRead, NULL, &iosb);
return "NtReadFile(hChildPipeRead) failed";
}
nRead = (uint32_t) iosb.Information;
ffStrbufAppendNS(buffer, nRead, str);
} while (nRead > 0);
exit:
{
DWORD exitCode = 0;
if (GetExitCodeProcess(hProcess, &exitCode) && exitCode != STILL_ACTIVE && exitCode != 0)
return "Child process exited with an error";
PROCESS_BASIC_INFORMATION info = {};
ULONG size;
if(NT_SUCCESS(NtQueryInformationProcess(hProcess, ProcessBasicInformation, &info, sizeof(info), &size)))
{
assert(size == sizeof(info));
if (info.ExitStatus != STILL_ACTIVE && info.ExitStatus != 0)
return "Child process exited with an error";
}
else
return "NtQueryInformationProcess(ProcessBasicInformation) failed";
}
return NULL;
@@ -203,15 +234,14 @@ exit:
bool ffProcessGetInfoWindows(uint32_t pid, uint32_t* ppid, FFstrbuf* pname, FFstrbuf* exe, const char** exeName, FFstrbuf* exePath, bool* gui)
{
FF_AUTO_CLOSE_FD HANDLE hProcess = pid == 0
? GetCurrentProcess()
: OpenProcess(PROCESS_QUERY_LIMITED_INFORMATION, FALSE, pid);
if (hProcess == NULL)
return false;
if (gui)
*gui = GetGuiResources(hProcess, GR_GDIOBJECTS) > 0;
FF_AUTO_CLOSE_FD HANDLE hProcess = NtCurrentProcess();
if(pid != 0)
{
if (!NT_SUCCESS(NtOpenProcess(&hProcess, PROCESS_QUERY_LIMITED_INFORMATION, &(OBJECT_ATTRIBUTES) {
.Length = sizeof(OBJECT_ATTRIBUTES),
}, &(CLIENT_ID) { .UniqueProcess = (HANDLE)(uintptr_t) pid })))
return false;
}
if(ppid)
{
@@ -225,23 +255,41 @@ bool ffProcessGetInfoWindows(uint32_t pid, uint32_t* ppid, FFstrbuf* pname, FFst
else
return false;
}
if(exe)
{
DWORD bufSize = exe->allocated;
if(QueryFullProcessImageNameA(hProcess, 0, exe->chars, &bufSize))
// TODO: It's possible to query the command line with `NtQueryInformationProcess(60/*ProcessCommandLineInformation*/)` since Windows 8.1
alignas(UNICODE_STRING) uint8_t buffer[4096];
ULONG size;
if(NT_SUCCESS(NtQueryInformationProcess(hProcess, ProcessImageFileNameWin32, &buffer, sizeof(buffer), &size)))
{
// We use full path here
// Querying command line of remote processes in Windows requires either WMI or ReadProcessMemory
exe->length = bufSize;
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;
+2 -2
View File
@@ -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;
+205 -70
View File
@@ -18,6 +18,7 @@ bool ffIsSmbiosValueSet(FFstrbuf* value)
!ffStrbufIgnCaseEqualS(value, "None") &&
!ffStrbufIgnCaseEqualS(value, "System Name") &&
!ffStrbufIgnCaseEqualS(value, "System Version") &&
!ffStrbufIgnCaseEqualS(value, "System SKU#") &&
!ffStrbufIgnCaseEqualS(value, "Default string") &&
!ffStrbufIgnCaseEqualS(value, "Undefined") &&
!ffStrbufIgnCaseEqualS(value, "Not Specified") &&
@@ -32,11 +33,29 @@ bool ffIsSmbiosValueSet(FFstrbuf* value)
!ffStrbufIgnCaseEqualS(value, "All Series") &&
!ffStrbufIgnCaseEqualS(value, "N/A") &&
!ffStrbufIgnCaseEqualS(value, "Unknown") &&
!ffStrbufIgnCaseEqualS(value, "Standard") &&
!ffStrbufIgnCaseEqualS(value, "0x0000")
!ffStrbufIgnCaseEqualS(value, "Standard") && ({
// Some SMBIOS implementations use "0x0000" to indicate an unset value, even for strings.
bool zero = ffStrbufStartsWithS(value, "0x0");
if (zero)
{
for (size_t i = 2; i < value->length; i++)
{
char c = value->chars[i];
if (c != '0')
{
zero = false;
break;
}
}
}
!zero;
})
;
}
static bool smbiosTableInitialized = false;
static FFSmbiosHeaderTable smbiosTable;
const FFSmbiosHeader* ffSmbiosNextEntry(const FFSmbiosHeader* header)
{
const char* p = ((const char*) header) + header->Length;
@@ -52,6 +71,74 @@ const FFSmbiosHeader* ffSmbiosNextEntry(const FFSmbiosHeader* header)
return (const FFSmbiosHeader*) (p + 1);
}
static bool parseSmbiosTable(const uint8_t* data, uint32_t length)
{
const FFSmbiosHeader* endOfTable = NULL;
FF_DEBUG("Parsing SMBIOS table structures with length %u bytes", length);
FF_MAYBE_UNUSED int structureCount = 0, totalCount = 0;
for (
const FFSmbiosHeader* header = (const FFSmbiosHeader*) data;
(const uint8_t*) header + sizeof(FFSmbiosHeader) < (const uint8_t*) data + length;
header = ffSmbiosNextEntry(header)
)
{
++totalCount;
endOfTable = header;
// This doesn't verify the entire structure (e.g. string section can still be truncated),
// but at least ensures the formatted section is valid and prevents infinite loops
// when the table is severely malformed.
if (__builtin_expect((const uint8_t*) header + header->Length > (const uint8_t*) data + length, false))
{
FF_DEBUG("Truncated SMBIOS structure at offset 0x%lx: length %u is too small",
(unsigned long)((const uint8_t*) header - data), header->Length);
break;
}
if (header->Type < FF_SMBIOS_TYPE_END_OF_TABLE)
{
if (!smbiosTable[header->Type])
{
smbiosTable[header->Type] = header;
FF_DEBUG("Found SMBIOS structure type %u, handle 0x%04X, length %u",
header->Type, header->Handle, header->Length);
structureCount++;
}
else
{
FF_DEBUG("Duplicate SMBIOS structure type %u, handle 0x%04X, length %u",
header->Type, header->Handle, header->Length);
}
}
else if (header->Type == FF_SMBIOS_TYPE_END_OF_TABLE)
{
FF_DEBUG("Reached SMBIOS end of type %u, handle 0x%04X, length %u",
header->Type, header->Handle, header->Length);
break;
}
else
{
FF_DEBUG("Found custom SMBIOS structure type %u, handle 0x%04X, length %u; ignoring",
header->Type, header->Handle, header->Length);
}
}
if (!endOfTable)
{
FF_DEBUG("No SMBIOS structures found in table");
return false;
}
FF_DEBUG("Parsed %d/%d SMBIOS structures, end-of-table (Type 127) %s",
structureCount,
totalCount,
endOfTable->Type == FF_SMBIOS_TYPE_END_OF_TABLE ? "found." : "not found! SMBIOS data may be malformed.");
smbiosTable[FF_SMBIOS_TYPE_END_OF_TABLE] = endOfTable;
return true;
}
#if defined(__linux__) || defined(__FreeBSD__) || defined(__NetBSD__) || defined(__sun) || defined(__HAIKU__) || defined(__OpenBSD__) || defined(__GNU__)
#include <fcntl.h>
#include <sys/stat.h>
@@ -71,17 +158,30 @@ const FFSmbiosHeader* ffSmbiosNextEntry(const FFSmbiosHeader* header)
#define loff_t off_t
#endif
#ifdef __linux__
bool ffGetSmbiosValue(const char* devicesPath, const char* classPath, FFstrbuf* buffer)
{
if (ffReadFileBuffer(devicesPath, buffer))
// /sys/class/dmi/id/* are all pseudo-files with very small content
// so reading the whole file at once is efficient
ffStrbufEnsureFixedLengthFree(buffer, 127);
ssize_t len = ffReadFileData(devicesPath, buffer->allocated - 1, buffer->chars);
if (len > 0)
{
assert(len < buffer->allocated);
buffer->chars[len] = '\0';
buffer->length = (uint32_t) len;
ffStrbufTrimRightSpace(buffer);
if(ffIsSmbiosValueSet(buffer))
return true;
}
if (ffReadFileBuffer(classPath, buffer))
len = ffReadFileData(classPath, buffer->allocated - 1, buffer->chars);
if (len > 0)
{
assert(len < buffer->allocated);
buffer->chars[len] = '\0';
buffer->length = (uint32_t) len;
ffStrbufTrimRightSpace(buffer);
if(ffIsSmbiosValueSet(buffer))
return true;
@@ -90,6 +190,7 @@ bool ffGetSmbiosValue(const char* devicesPath, const char* classPath, FFstrbuf*
ffStrbufClear(buffer);
return false;
}
#endif
typedef struct FFSmbios20EntryPoint
{
@@ -109,7 +210,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
{
@@ -137,10 +238,10 @@ typedef union FFSmbiosEntryPoint
const FFSmbiosHeaderTable* ffGetSmbiosHeaderTable()
{
static FFstrbuf buffer;
static FFSmbiosHeaderTable table;
if (buffer.chars == NULL)
if (!smbiosTableInitialized)
{
smbiosTableInitialized = true;
FF_DEBUG("Initializing SMBIOS buffer");
ffStrbufInit(&buffer);
#if !__HAIKU__ && !__OpenBSD__ && !__DragonFly__ && !__GNU__
@@ -401,29 +502,8 @@ const FFSmbiosHeaderTable* ffGetSmbiosHeaderTable()
}
#endif
FF_DEBUG("Parsing SMBIOS table structures");
FF_MAYBE_UNUSED int structureCount = 0;
for (
const FFSmbiosHeader* header = (const FFSmbiosHeader*) buffer.chars;
(const uint8_t*) header < (const uint8_t*) buffer.chars + buffer.length;
header = ffSmbiosNextEntry(header)
)
{
if (header->Type < FF_SMBIOS_TYPE_END_OF_TABLE)
{
if (!table[header->Type]) {
table[header->Type] = header;
FF_DEBUG("Found SMBIOS structure type %u, handle 0x%04X, length %u",
header->Type, header->Handle, header->Length);
structureCount++;
}
}
else if (header->Type == FF_SMBIOS_TYPE_END_OF_TABLE) {
FF_DEBUG("Reached end-of-table marker");
break;
}
}
FF_DEBUG("Parsed %d SMBIOS structures", structureCount);
if (!parseSmbiosTable((const uint8_t*) buffer.chars, buffer.length))
ffStrbufClear(&buffer);
}
if (buffer.length == 0) {
@@ -431,10 +511,10 @@ const FFSmbiosHeaderTable* ffGetSmbiosHeaderTable()
return NULL;
}
return &table;
return &smbiosTable;
}
#elif defined(_WIN32)
#include <windows.h>
#include "common/windows/nt.h"
#pragma GCC diagnostic ignored "-Wmultichar"
@@ -450,56 +530,111 @@ typedef struct FFRawSmbiosData
const FFSmbiosHeaderTable* ffGetSmbiosHeaderTable()
{
static FFRawSmbiosData* buffer;
static FFSmbiosHeaderTable table;
static SYSTEM_FIRMWARE_TABLE_INFORMATION* buffer;
if (!buffer)
if (!smbiosTableInitialized)
{
smbiosTableInitialized = true;
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;
}
FF_DEBUG("Firmware table size: %u bytes", bufSize);
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: %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);
FF_DEBUG("Successfully retrieved SMBIOS data: version %u.%u, length %u bytes",
buffer->SMBIOSMajorVersion, buffer->SMBIOSMinorVersion, buffer->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;
header = ffSmbiosNextEntry(header)
)
if (!NT_SUCCESS(NtQuerySystemInformation(SystemFirmwareTableInformation, buffer, bufSize, &bufSize)))
{
if (header->Type < FF_SMBIOS_TYPE_END_OF_TABLE)
{
if (!table[header->Type]) {
table[header->Type] = header;
FF_DEBUG("Found SMBIOS structure type %u, handle 0x%04X, length %u",
header->Type, header->Handle, header->Length);
structureCount++;
}
}
else if (header->Type == FF_SMBIOS_TYPE_END_OF_TABLE) {
FF_DEBUG("Reached end-of-table marker");
break;
}
FF_DEBUG("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",
rawData->SMBIOSMajorVersion, rawData->SMBIOSMinorVersion, rawData->Length);
if (!parseSmbiosTable(rawData->SMBIOSTableData, rawData->Length))
{
free(buffer);
buffer = NULL;
return NULL;
}
FF_DEBUG("Parsed %d SMBIOS structures", structureCount);
}
return &table;
if (!buffer) {
FF_DEBUG("No valid SMBIOS data available");
return NULL;
}
return &smbiosTable;
}
#elif defined(__APPLE__)
#include "common/apple/cf_helpers.h"
const FFSmbiosHeaderTable* ffGetSmbiosHeaderTable()
{
static CFDataRef smbiosDataBuffer;
if (!smbiosTableInitialized)
{
smbiosTableInitialized = true;
FF_DEBUG("Initializing SMBIOS buffer on Apple platform");
FF_IOOBJECT_AUTO_RELEASE io_registry_entry_t registryEntry = IOServiceGetMatchingService(MACH_PORT_NULL, IOServiceMatching("AppleSMBIOS"));
if (!registryEntry)
{
FF_DEBUG("IOServiceGetMatchingService() failed to find AppleSMBIOS");
return NULL;
}
FF_DEBUG("AppleSMBIOS service found, retrieving SMBIOS data");
smbiosDataBuffer = IORegistryEntryCreateCFProperty(registryEntry, CFSTR("SMBIOS"), kCFAllocatorDefault, kNilOptions);
if (!smbiosDataBuffer)
{
FF_DEBUG("IORegistryEntryCreateCFProperty() failed to get SMBIOS data");
return NULL;
}
if (CFGetTypeID(smbiosDataBuffer) != CFDataGetTypeID())
{
FF_DEBUG("Unexpected SMBIOS data type: expected CFData");
CFRelease(smbiosDataBuffer);
smbiosDataBuffer = NULL;
return NULL;
}
FF_DEBUG("Successfully retrieved SMBIOS data: %lu bytes", CFDataGetLength(smbiosDataBuffer));
if (!parseSmbiosTable((const uint8_t*) CFDataGetBytePtr(smbiosDataBuffer), (uint32_t) CFDataGetLength(smbiosDataBuffer)))
{
CFRelease(smbiosDataBuffer);
smbiosDataBuffer = NULL;
return NULL;
}
}
if (!smbiosDataBuffer)
{
FF_DEBUG("No valid SMBIOS data available");
return NULL;
}
return &smbiosTable;
}
#endif
+2
View File
@@ -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;
+1 -1
View File
@@ -89,7 +89,7 @@ FFTimeGetAgeResult ffTimeGetAge(uint64_t birthMs, uint64_t nowMs)
static void ffTimeInitQpcMultiplier(void)
{
LARGE_INTEGER frequency;
QueryPerformanceFrequency(&frequency);
RtlQueryPerformanceFrequency(&frequency);
ffQpcMultiplier = 1000. / (double) frequency.QuadPart;
}
#endif
+102 -42
View File
@@ -7,6 +7,7 @@
#include <fileapi.h>
#include <handleapi.h>
#include <io.h>
#include "common/windows/nt.h"
typedef HANDLE FFNativeFD;
#define FF_INVALID_FD INVALID_HANDLE_VALUE
#else
@@ -15,6 +16,7 @@
#include <sys/stat.h>
#include <errno.h>
#include <limits.h>
#include <fcntl.h>
typedef int FFNativeFD;
#define FF_INVALID_FD (-1)
// procfs's file can be changed between read calls such as /proc/meminfo and /proc/uptime.
@@ -22,6 +24,51 @@
#define PROC_FILE_BUFFSIZ (32 * 1024)
#endif
#ifdef _WIN32
#ifndef O_CLOEXEC
#define O_CLOEXEC 0
#endif
#ifndef O_RDONLY
#define O_RDONLY 0
#endif
#ifndef O_DIRECTORY
#define O_DIRECTORY 0200000
#endif
// Only O_RDONLY is supported
HANDLE openat(HANDLE dfd, const char* fileName, int oflag);
HANDLE openatW(HANDLE dfd, const wchar_t* fileName, uint16_t fileNameLen, bool directory);
#endif
static inline bool ffIsValidNativeFD(FFNativeFD fd)
{
#ifndef _WIN32
return fd >= 0;
#else
// https://devblogs.microsoft.com/oldnewthing/20040302-00/?p=40443
return fd != INVALID_HANDLE_VALUE && fd != NULL;
#endif
}
FF_C_NONNULL(1)
static inline bool wrapClose(FFNativeFD* pfd)
{
assert(pfd);
if (!ffIsValidNativeFD(*pfd))
return false;
#ifndef _WIN32
close(*pfd);
#else
NtClose(*pfd);
#endif
return true;
}
#define FF_AUTO_CLOSE_FD __attribute__((__cleanup__(wrapClose)))
static inline FFNativeFD FFUnixFD2NativeFD(int unixfd)
{
#ifndef _WIN32
@@ -71,17 +118,60 @@ static inline ssize_t ffReadFDData(FFNativeFD fd, size_t dataSize, void* data)
#endif
}
FF_C_NONNULL(1, 3)
ssize_t ffReadFileData(const char* fileName, size_t dataSize, void* data);
FF_C_NONNULL(2, 4)
ssize_t ffReadFileDataRelative(FFNativeFD dfd, const char* fileName, size_t dataSize, void* data);
FF_C_NONNULL(2)
bool ffAppendFDBuffer(FFNativeFD fd, FFstrbuf* buffer);
FF_C_NONNULL(1, 3)
static inline ssize_t ffReadFileData(const char* fileName, size_t dataSize, void* data)
{
FFNativeFD FF_AUTO_CLOSE_FD fd =
#ifndef _WIN32
open(fileName, O_RDONLY | O_CLOEXEC);
#else
CreateFileA(fileName, GENERIC_READ, FILE_SHARE_READ, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL);
#endif
if (!ffIsValidNativeFD(fd))
return -1;
return ffReadFDData(fd, dataSize, data);
}
FF_C_NONNULL(2, 4)
static inline ssize_t ffReadFileDataRelative(FFNativeFD dfd, const char* fileName, size_t dataSize, void* data)
{
FFNativeFD FF_AUTO_CLOSE_FD fd = openat(dfd, fileName, O_RDONLY | O_CLOEXEC);
if (!ffIsValidNativeFD(fd))
return -1;
return ffReadFDData(fd, dataSize, data);
}
FF_C_NONNULL(1, 2)
bool ffAppendFileBuffer(const char* fileName, FFstrbuf* buffer);
static inline bool ffAppendFileBuffer(const char* fileName, FFstrbuf* buffer)
{
FFNativeFD FF_AUTO_CLOSE_FD fd =
#ifndef _WIN32
open(fileName, O_RDONLY | O_CLOEXEC);
#else
CreateFileA(fileName, GENERIC_READ, FILE_SHARE_READ, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL);
#endif
if (!ffIsValidNativeFD(fd))
return false;
return ffAppendFDBuffer(fd, buffer);
}
FF_C_NONNULL(2, 3)
bool ffAppendFileBufferRelative(FFNativeFD dfd, const char* fileName, FFstrbuf* buffer);
static inline bool ffAppendFileBufferRelative(FFNativeFD dfd, const char* fileName, FFstrbuf* buffer)
{
FFNativeFD FF_AUTO_CLOSE_FD fd = openat(dfd, fileName, O_RDONLY | O_CLOEXEC);
if (!ffIsValidNativeFD(fd))
return false;
return ffAppendFDBuffer(fd, buffer);
}
FF_C_NONNULL(2)
static inline bool ffReadFDBuffer(FFNativeFD fd, FFstrbuf* buffer)
@@ -117,7 +207,11 @@ static inline bool ffPathExists(const char* path, FFPathType pathType)
{
#ifdef _WIN32
DWORD attr = GetFileAttributesA(path);
wchar_t wPath[MAX_PATH];
if (!NT_SUCCESS(RtlUTF8ToUnicodeN(wPath, (ULONG) sizeof(wPath), NULL, path, (ULONG)strlen(path) + 1)))
return false;
DWORD attr = GetFileAttributesW(wPath);
if(attr == INVALID_FILE_ATTRIBUTES)
return false;
@@ -178,34 +272,6 @@ static inline void ffUnsuppressIO(bool* suppressed)
void ffListFilesRecursively(const char* path, bool pretty);
static inline bool ffIsValidNativeFD(FFNativeFD fd)
{
#ifndef _WIN32
return fd >= 0;
#else
// https://devblogs.microsoft.com/oldnewthing/20040302-00/?p=40443
return fd != INVALID_HANDLE_VALUE && fd != NULL;
#endif
}
FF_C_NONNULL(1)
static inline bool wrapClose(FFNativeFD* pfd)
{
assert(pfd);
if (!ffIsValidNativeFD(*pfd))
return false;
#ifndef _WIN32
close(*pfd);
#else
CloseHandle(*pfd);
#endif
return true;
}
#define FF_AUTO_CLOSE_FD __attribute__((__cleanup__(wrapClose)))
FF_C_NONNULL(1)
static inline bool wrapFclose(FILE** pfile)
{
@@ -257,9 +323,3 @@ static inline bool ffSearchUserConfigFile(const FFlist* configDirs, const char*
FFNativeFD ffGetNullFD(void);
bool ffRemoveFile(const char* fileName);
#ifdef _WIN32
// Only O_RDONLY is supported
HANDLE openat(HANDLE dfd, const char* fileName, bool directory);
HANDLE openatW(HANDLE dfd, const wchar_t* fileName, uint16_t fileNameLen, bool directory);
#endif
+17 -15
View File
@@ -6,11 +6,10 @@
#ifndef FF_DISABLE_DLOPEN
#if defined(_WIN32)
#include <libloaderapi.h>
#define FF_DLOPEN_FLAGS 0
FF_C_NODISCARD static inline void* dlopen(const char* path, int mode) { FF_UNUSED(mode); return LoadLibraryA(path); }
FF_C_NODISCARD static inline void* dlsym(void* handle, const char* symbol) { return (void*) GetProcAddress((HMODULE)handle, symbol); }
static inline int dlclose(void* handle) { return !FreeLibrary((HMODULE)handle); }
FF_C_NODISCARD void* dlopen(const char* path, int mode);
FF_C_NODISCARD void* dlsym(void* handle, const char* symbol);
int dlclose(void* handle);
#else
#include <dlfcn.h>
#endif
@@ -30,6 +29,10 @@ static inline void ffLibraryUnload(void** handle)
dlclose(*handle);
}
#if __cplusplus
#define __auto_type auto
#endif
#define FF_LIBRARY_SYMBOL(symbolName) \
__typeof__(&symbolName) ff ## symbolName;
@@ -38,13 +41,8 @@ static inline void ffLibraryUnload(void** handle)
if(libraryObjectName == NULL) \
return returnValue;
#if _WIN32
#define FF_LIBRARY_LOAD_MESSAGE(libraryObjectName, libraryFileName, maxVersion, ...) \
FF_LIBRARY_LOAD(libraryObjectName, "LoadLibraryA(" libraryFileName ") failed", libraryFileName, maxVersion, ##__VA_ARGS__)
#else
#define FF_LIBRARY_LOAD_MESSAGE(libraryObjectName, libraryFileName, maxVersion, ...) \
FF_LIBRARY_LOAD(libraryObjectName, "dlopen(" libraryFileName ") failed", libraryFileName, maxVersion, ##__VA_ARGS__)
#endif
#define FF_LIBRARY_LOAD_SYMBOL_ADDRESS(library, symbolMapping, symbolName, returnValue) \
symbolMapping = (__typeof__(&symbolName)) dlsym(library, #symbolName); \
@@ -52,13 +50,13 @@ static inline void ffLibraryUnload(void** handle)
return returnValue;
#define FF_LIBRARY_LOAD_SYMBOL(library, symbolName, returnValue) \
__typeof__(&symbolName) FF_LIBRARY_LOAD_SYMBOL_ADDRESS(library, ff ## symbolName, symbolName, returnValue);
__auto_type FF_LIBRARY_LOAD_SYMBOL_ADDRESS(library, ff ## symbolName, symbolName, returnValue);
#define FF_LIBRARY_LOAD_SYMBOL_LAZY(library, symbolName) \
__typeof__(&symbolName) ff ## symbolName = (__typeof__(&symbolName)) dlsym(library, #symbolName);
__auto_type ff ## symbolName = (__typeof__(&symbolName)) dlsym(library, #symbolName);
#define FF_LIBRARY_LOAD_SYMBOL_MESSAGE(library, symbolName) \
__typeof__(&symbolName) FF_LIBRARY_LOAD_SYMBOL_ADDRESS(library, ff ## symbolName, symbolName, "dlsym " #symbolName " failed");
__auto_type FF_LIBRARY_LOAD_SYMBOL_ADDRESS(library, ff ## symbolName, symbolName, "dlsym " #symbolName " failed");
#define FF_LIBRARY_LOAD_SYMBOL_VAR(library, varName, symbolName, returnValue) \
FF_LIBRARY_LOAD_SYMBOL_ADDRESS(library, (varName).ff ## symbolName, symbolName, returnValue);
@@ -88,13 +86,13 @@ void* ffLibraryLoad(const char* path, int maxVersion, ...);
symbolMapping = (__typeof__(&symbolName)) &symbolName;
#define FF_LIBRARY_LOAD_SYMBOL(library, symbolName, returnValue) \
FF_MAYBE_UNUSED __typeof__(&symbolName) FF_LIBRARY_LOAD_SYMBOL_ADDRESS(library, ff ## symbolName, symbolName, returnValue);
FF_MAYBE_UNUSED __auto_type FF_LIBRARY_LOAD_SYMBOL_ADDRESS(library, ff ## symbolName, symbolName, returnValue);
#define FF_LIBRARY_LOAD_SYMBOL_LAZY(library, symbolName) \
FF_MAYBE_UNUSED __typeof__(&symbolName) ff ## symbolName = (__typeof__(&symbolName)) &symbolName;
FF_MAYBE_UNUSED __auto_type ff ## symbolName = (__typeof__(&symbolName)) &symbolName;
#define FF_LIBRARY_LOAD_SYMBOL_MESSAGE(library, symbolName) \
FF_MAYBE_UNUSED __typeof__(&symbolName) FF_LIBRARY_LOAD_SYMBOL_ADDRESS(library, ff ## symbolName, symbolName, "dlsym " #symbolName " failed");
FF_MAYBE_UNUSED __auto_type FF_LIBRARY_LOAD_SYMBOL_ADDRESS(library, ff ## symbolName, symbolName, "dlsym " #symbolName " failed");
#define FF_LIBRARY_LOAD_SYMBOL_VAR(library, varName, symbolName, returnValue) \
FF_LIBRARY_LOAD_SYMBOL_ADDRESS(library, (varName).ff ## symbolName, symbolName, returnValue);
@@ -106,3 +104,7 @@ void* ffLibraryLoad(const char* path, int maxVersion, ...);
FF_LIBRARY_LOAD_SYMBOL_ADDRESS(library, (varName)->ff ## symbolName, symbolName, returnValue);
#endif
#if _WIN32
void* ffLibraryGetModule(const wchar_t* libraryFileName);
#endif
+15
View File
@@ -0,0 +1,15 @@
#pragma once
#include <stddef.h>
#ifdef __cplusplus
extern "C" {
#endif
// `memrchr` is a GNU extension and may not be declared by system headers even when the symbol exists.
// Declare it unconditionally; the build system provides a fallback implementation when missing.
void* memrchr(const void* s, int c, size_t n);
#ifdef __cplusplus
}
#endif
+7
View File
@@ -13,3 +13,10 @@ static inline bool ffIsAbsolutePath(const char* path)
return path[0] == '/';
#endif
}
#if _WIN32
char* frealpath(void* __restrict hFile, char* __restrict resolved_name /*MAX_PATH*/);
char* realpath(const char* __restrict file_name, char* __restrict resolved_name /*MAX_PATH*/);
ssize_t freadlink(void* hFile, char* buf, size_t bufsiz);
ssize_t readlink(const char* path, char* buf, size_t bufsiz);
#endif
+9 -1
View File
@@ -63,14 +63,22 @@ typedef enum __attribute__((__packed__)) FFSmbiosType // : uint8_t
FF_SMBIOS_TYPE_STRING_PROPERTY = 46,
FF_SMBIOS_TYPE_INACTIVE = 126,
FF_SMBIOS_TYPE_END_OF_TABLE = 127,
FF_SMBIOS_TYPE__MAX,
// system- and OEM-specific information 128~256
} FFSmbiosType;
static_assert(sizeof(FFSmbiosType) == 1, "FFSmbiosType should be 1 byte");
typedef struct FFSmbiosHeader
{
// Type of SMBIOS structure. Do NOT test `Type == FF_SMBIOS_END_OF_TABLE` to determine the end of the table,
// as malformed tables may be missing the end-of-table marker.
// **Use FFSmbiosHeaderTable[FF_SMBIOS_TYPE_END_OF_TABLE] pointer instead.**
FFSmbiosType Type;
// Length of formatted section, excluding unformatted string section
// Must be at least 4 (sizeof(FFSmbiosHeader)) to be valid
uint8_t Length;
// Unique handle, used to reference this structure from other structures.
// Not guaranteed to be consistent across reboots or even multiple reads of the same table.
uint16_t Handle;
} __attribute__((__packed__)) FFSmbiosHeader;
static_assert(sizeof(FFSmbiosHeader) == 4, "FFSmbiosHeader should be 4 bytes");
@@ -84,7 +92,7 @@ static inline const char* ffSmbiosLocateString(const char* start, uint8_t index
return start;
}
typedef const FFSmbiosHeader* FFSmbiosHeaderTable[FF_SMBIOS_TYPE_END_OF_TABLE];
typedef const FFSmbiosHeader* FFSmbiosHeaderTable[FF_SMBIOS_TYPE__MAX];
const FFSmbiosHeader* ffSmbiosNextEntry(const FFSmbiosHeader* header);
const FFSmbiosHeaderTable* ffGetSmbiosHeaderTable();
+17
View File
@@ -80,6 +80,23 @@ static inline bool ffCharIsDigit(char c)
return '0' <= c && c <= '9';
}
static inline bool ffCharIsHexDigit(char c)
{
return ffCharIsDigit(c) || ('a' <= c && c <= 'f') || ('A' <= c && c <= 'F');
}
static inline int8_t ffHexCharToInt(char c)
{
if (ffCharIsDigit(c))
return (int8_t) (c - '0');
else if ('a' <= c && c <= 'f')
return (int8_t) (c - 'a' + 10);
else if ('A' <= c && c <= 'F')
return (int8_t) (c - 'A' + 10);
else
return -1;
}
// Copies at most (dstBufSiz - 1) bytes from src to dst; dst is always null-terminated
static inline char* ffStrCopy(char* __restrict__ dst, const char* __restrict__ src, size_t dstBufSiz)
{
+5 -5
View File
@@ -4,7 +4,7 @@
#ifdef FF_HAVE_THREADS
#if defined(_WIN32)
#include <handleapi.h>
#include <winternl.h>
#include <synchapi.h>
#include <process.h>
#include <processthreadsapi.h>
@@ -18,16 +18,16 @@
}
#define FF_THREAD_ENTRY_DECL_WRAPPER(fn, paramType) static __stdcall unsigned fn ## ThreadMain (void* data) { fn((paramType)data); return 0; }
#define FF_THREAD_ENTRY_DECL_WRAPPER_NOPARAM(fn) static __stdcall unsigned fn ## ThreadMain () { fn(); return 0; }
static inline void ffThreadDetach(FFThreadType thread) { CloseHandle(thread); }
static inline void ffThreadDetach(FFThreadType thread) { NtClose(thread); }
static inline bool ffThreadJoin(FFThreadType thread, uint32_t timeout)
{
if (WaitForSingleObject(thread, timeout == 0 ? (DWORD) -1 : timeout) != 0 /*WAIT_OBJECT_0*/)
if (NtWaitForSingleObject(thread, FALSE, timeout == 0 ? NULL : &(LARGE_INTEGER) { .QuadPart = (int64_t) timeout * -10000 }) != STATUS_WAIT_0)
{
TerminateThread(thread, (DWORD) -1);
CloseHandle(thread);
NtClose(thread);
return false;
}
CloseHandle(thread);
NtClose(thread);
return true;
}
#else
+14 -14
View File
@@ -4,17 +4,9 @@
#include <stdint.h>
#include <time.h>
#ifdef _WIN32
#include <ntdef.h>
#include <ntstatus.h>
#include "common/windows/nt.h"
#include <profileapi.h>
#include <sysinfoapi.h>
NTSYSCALLAPI
NTSTATUS
NTAPI
NtDelayExecution(
_In_ BOOLEAN Alertable,
_In_ PLARGE_INTEGER DelayInterval);
#elif defined(__HAIKU__)
#include <OS.h>
#endif
@@ -26,7 +18,7 @@ static inline double ffTimeGetTick(void) //In msec
#ifdef _WIN32
extern double ffQpcMultiplier;
LARGE_INTEGER start;
QueryPerformanceCounter(&start);
RtlQueryPerformanceCounter(&start);
return (double) start.QuadPart * ffQpcMultiplier;
#elif defined(__HAIKU__)
return (double) system_time() / 1000.;
@@ -37,12 +29,20 @@ static inline double ffTimeGetTick(void) //In msec
#endif
}
#if _WIN32
static inline uint64_t ffFileTimeToUnixMs(uint64_t value)
{
if (__builtin_expect(__builtin_usubll_overflow(value, 116444736000000000ull, &value), false))
return 0;
return value / 10000ull;
}
#endif
static inline uint64_t ffTimeGetNow(void)
{
#ifdef _WIN32
uint64_t timeNow;
GetSystemTimeAsFileTime((FILETIME*) &timeNow);
return (timeNow - 116444736000000000ull) / 10000ull;
uint64_t timeNow = ffKSystemTimeToUInt64(&SharedUserData->SystemTime);
return ffFileTimeToUnixMs((uint64_t) timeNow);
#elif defined(__HAIKU__)
return (uint64_t) real_time_clock_usecs() / 1000u;
#else
@@ -58,7 +58,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
+5 -2
View File
@@ -17,7 +17,8 @@ static const char* doInitCom()
return "CoInitializeEx() failed";
// Set general COM security levels
if (FAILED(CoInitializeSecurity(
HRESULT hRes = CoInitializeSecurity(
NULL,
-1, // COM authentication
NULL, // Authentication services
@@ -27,7 +28,9 @@ static const char* doInitCom()
NULL, // Authentication info
EOAC_NONE, // Additional capabilities
NULL // Reserved
)))
);
if (FAILED(hRes) && hRes != RPC_E_TOO_LATE /* Has been set by a random dll */)
{
CoUninitialize();
return "CoInitializeSecurity() failed";
+4 -9
View File
@@ -2,21 +2,16 @@
<assembly xmlns="urn:schemas-microsoft-com:asm.v1" manifestVersion="1.0" xmlns:asmv3="urn:schemas-microsoft-com:asm.v3">
<compatibility xmlns="urn:schemas-microsoft-com:compatibility.v1">
<application>
<!--This Id value indicates the application supports Windows 7 functionality-->
<supportedOS Id="{35138b9a-5d96-4fbd-8e2d-a2440225f93a}"/>
<!--This Id value indicates the application supports Windows 8 functionality-->
<supportedOS Id="{4a2f28e3-53b9-4441-ba9c-d69d4a4a6e38}"/>
<!--This Id value indicates the application supports Windows 8.1 functionality-->
<supportedOS Id="{1f676c76-80e1-4239-95bb-83d0f6d0da78}"/>
<!--This Id value indicates the application supports Windows 10, 11 functionality-->
<supportedOS Id="{8e0f7a12-bfb3-4fe8-b9a5-48fd50a15a9a}"/>
<supportedOS Id="{1f676c76-80e1-4239-95bb-83d0f6d0da78}"/><!-- 8.1 -->
<supportedOS Id="{8e0f7a12-bfb3-4fe8-b9a5-48fd50a15a9a}"/><!-- 10/11 -->
</application>
</compatibility>
<assemblyIdentity type="win32" name="com.github.fastfetch" version="0.0.0.0"/>
<asmv3:application>
<asmv3:windowsSettings>
<activeCodePage xmlns="http://schemas.microsoft.com/SMI/2019/WindowsSettings">UTF-8</activeCodePage>
<dpiAware xmlns="http://schemas.microsoft.com/SMI/2005/WindowsSettings">false</dpiAware>
<dpiAware xmlns="http://schemas.microsoft.com/SMI/2005/WindowsSettings">true/pm</dpiAware>
<dpiAwareness xmlns="http://schemas.microsoft.com/SMI/2016/WindowsSettings">PerMonitor</dpiAwareness>
<heapType xmlns="http://schemas.microsoft.com/SMI/2020/WindowsSettings">SegmentHeap</heapType>
</asmv3:windowsSettings>
</asmv3:application>
+1357 -221
View File
File diff suppressed because it is too large Load Diff
+362 -111
View File
@@ -1,17 +1,22 @@
#include "registry.h"
#include "unicode.h"
#include "common/mallocHelper.h"
#include "common/debug.h"
#include "common/windows/nt.h"
static const char* hKey2Str(HKEY hKey)
#include <stdalign.h>
#include <ntstatus.h>
static HANDLE hRootKeys[8 /*(uintptr_t) HKEY_CURRENT_USER_LOCAL_SETTINGS - (uintptr_t) HKEY_CLASSES_ROOT + 1*/];
static const char* hKey2Str(HANDLE hRootKey)
{
#define HKEY_CASE(compareKey) if(hKey == compareKey) return #compareKey;
#define HKEY_CASE(compareKey) if(hRootKey == hRootKeys[(uintptr_t)compareKey - (uintptr_t)HKEY_CLASSES_ROOT]) return #compareKey;
HKEY_CASE(HKEY_CLASSES_ROOT)
HKEY_CASE(HKEY_CURRENT_USER)
HKEY_CASE(HKEY_LOCAL_MACHINE)
HKEY_CASE(HKEY_USERS)
HKEY_CASE(HKEY_PERFORMANCE_DATA)
HKEY_CASE(HKEY_PERFORMANCE_TEXT)
HKEY_CASE(HKEY_PERFORMANCE_NLSTEXT)
HKEY_CASE(HKEY_CURRENT_CONFIG)
HKEY_CASE(HKEY_DYN_DATA)
HKEY_CASE(HKEY_CURRENT_USER_LOCAL_SETTINGS)
@@ -20,152 +25,398 @@ static const char* hKey2Str(HKEY hKey)
return "UNKNOWN";
}
bool ffRegOpenKeyForRead(HKEY hKey, const wchar_t* subKeyW, HKEY* result, FFstrbuf* error)
HANDLE ffRegGetRootKeyHandle(HKEY hKey)
{
if(RegOpenKeyExW(hKey, subKeyW, 0, KEY_READ, result) != ERROR_SUCCESS)
assert(hKey);
assert((uintptr_t) hKey >= (uintptr_t) HKEY_CLASSES_ROOT && (uintptr_t) hKey <= (uintptr_t) HKEY_CURRENT_USER_LOCAL_SETTINGS);
FF_DEBUG("Getting root key handle for HKEY %08llx", (uint64_t)(uintptr_t) hKey);
HANDLE result = hRootKeys[(uintptr_t) hKey - (uintptr_t) HKEY_CLASSES_ROOT];
if (result)
{
if(error)
FF_DEBUG("Found cached root key handle for %s -> %p", hKey2Str(result), result);
return result;
}
switch ((uintptr_t) hKey)
{
case (uintptr_t) HKEY_CURRENT_USER: {
NTSTATUS status = RtlOpenCurrentUser(KEY_READ, &result);
if (!NT_SUCCESS(status))
{
FF_DEBUG("RtlOpenCurrentUser() failed: %s", ffDebugNtStatus(status));
return NULL;
}
break;
}
case (uintptr_t) HKEY_LOCAL_MACHINE: {
UNICODE_STRING path = RTL_CONSTANT_STRING(L"\\Registry\\Machine");
NTSTATUS status = NtOpenKey(&result, KEY_READ, &(OBJECT_ATTRIBUTES) {
.Length = sizeof(OBJECT_ATTRIBUTES),
.RootDirectory = NULL,
.ObjectName = &path,
.Attributes = OBJ_CASE_INSENSITIVE,
});
if (!NT_SUCCESS(status))
{
FF_DEBUG("NtOpenKey(%ls) failed: %s (0x%08lx)", path.Buffer, ffDebugNtStatus(status), status);
return NULL;
}
break;
}
default:
// Unsupported
FF_DEBUG("Unsupported root key: %p", hKey);
assert(false);
return NULL;
}
hRootKeys[(uintptr_t) hKey - (uintptr_t) HKEY_CLASSES_ROOT] = result;
FF_DEBUG("Opened root key %s -> %p", hKey2Str(result), result);
return result;
}
bool ffRegOpenSubkeyForRead(HANDLE hKey, const wchar_t* subKeyW, HANDLE* result, FFstrbuf* error)
{
assert(hKey);
assert(subKeyW);
assert(result);
FF_DEBUG("Opening subkey %s\\%ls for read", hKey2Str(hKey), subKeyW);
USHORT subKeyLen = (USHORT) (wcslen(subKeyW) * sizeof(wchar_t));
if (!NT_SUCCESS(NtOpenKey(result, KEY_READ, &(OBJECT_ATTRIBUTES) {
.Length = sizeof(OBJECT_ATTRIBUTES),
.RootDirectory = hKey,
.ObjectName = &(UNICODE_STRING) {
.Length = subKeyLen,
.MaximumLength = subKeyLen + (USHORT) sizeof(wchar_t),
.Buffer = (wchar_t*) subKeyW,
},
})))
{
FF_DEBUG("NtOpenKey(%s\\<subkey>) failed", hKey2Str(hKey));
if (error)
{
FF_STRBUF_AUTO_DESTROY subKeyA = ffStrbufCreateWS(subKeyW);
ffStrbufAppendF(error, "RegOpenKeyExW(%s\\%s) failed", hKey2Str(hKey), subKeyA.chars);
ffStrbufAppendF(error, "NtOpenKey(%s\\%s) failed", hKey2Str(hKey), subKeyA.chars);
}
return false;
}
FF_DEBUG("Opened subkey under %s -> %p", hKey2Str(hKey), *result);
return true;
}
bool ffRegReadStrbuf(HKEY hKey, const wchar_t* valueNameW, FFstrbuf* result, FFstrbuf* error)
static bool processRegValue(const FFRegValueArg* arg, const ULONG regType, const void* regData, ULONG regDataLen, FFstrbuf* error)
{
DWORD bufSize; //with tailing '\0'
if(RegGetValueW(hKey, NULL, valueNameW, RRF_RT_REG_SZ, NULL, NULL, &bufSize) != ERROR_SUCCESS)
switch (arg->type)
{
if(error)
case FF_ARG_TYPE_STRBUF:
{
if(!valueNameW)
valueNameW = L"(default)";
FF_STRBUF_AUTO_DESTROY valueNameA = ffStrbufCreateWS(valueNameW);
ffStrbufAppendF(error, "RegGetValueW(%s, NULL, RRF_RT_REG_SZ) failed", valueNameA.chars);
if (regType != REG_SZ && regType != REG_EXPAND_SZ)
goto type_mismatch;
FFstrbuf* strbuf = (FFstrbuf*) arg->value;
uint32_t strLen = regDataLen / sizeof(wchar_t);
if (strLen == 0)
ffStrbufClear(strbuf);
else
{
const wchar_t* ws = (const wchar_t*) regData;
if (ws[strLen - 1] == L'\0')
--strLen;
ffStrbufSetNWS(strbuf, strLen, ws);
}
break;
}
return false;
}
assert(bufSize >= sizeof(wchar_t));
wchar_t* FF_AUTO_FREE resultW = (wchar_t*)malloc(bufSize);
if(RegGetValueW(hKey, NULL, valueNameW, RRF_RT_REG_SZ, NULL, resultW, &bufSize) != ERROR_SUCCESS)
{
if(error)
case FF_ARG_TYPE_UINT:
case FF_ARG_TYPE_UINT64:
case FF_ARG_TYPE_UINT16:
case FF_ARG_TYPE_UINT8:
case FF_ARG_TYPE_BOOL:
{
if(!valueNameW)
valueNameW = L"(default)";
FF_STRBUF_AUTO_DESTROY valueNameA = ffStrbufCreateWS(valueNameW);
ffStrbufAppendF(error, "RegGetValueW(%s, result, RRF_RT_REG_SZ) failed", valueNameA.chars);
uint64_t value = 0;
if (regType == REG_DWORD)
{
if (regDataLen < sizeof(uint32_t))
goto type_mismatch;
value = *(uint32_t*) regData;
}
else if (regType == REG_QWORD)
{
if (regDataLen < sizeof(uint64_t))
goto type_mismatch;
value = *(uint64_t*) regData;
}
else
goto type_mismatch;
if (arg->type == FF_ARG_TYPE_UINT) *(uint32_t*) arg->value = (uint32_t) value;
else if (arg->type == FF_ARG_TYPE_UINT64) *(uint64_t*) arg->value = (uint64_t) value;
else if (arg->type == FF_ARG_TYPE_UINT16) *(uint16_t*) arg->value = (uint16_t) value;
else if (arg->type == FF_ARG_TYPE_UINT8) *(uint8_t*) arg->value = (uint8_t) value;
else if (arg->type == FF_ARG_TYPE_BOOL) *(bool*) arg->value = value != 0;
break;
}
return false;
case FF_ARG_TYPE_FLOAT:
{
if (regDataLen < sizeof(float))
goto type_mismatch;
*(float*) arg->value = *(float*) regData;
break;
}
case FF_ARG_TYPE_DOUBLE:
{
if (regDataLen < sizeof(double))
goto type_mismatch;
*(double*) arg->value = *(double*) regData;
break;
}
case FF_ARG_TYPE_LIST:
{
if (regType != REG_MULTI_SZ)
goto type_mismatch;
FFlist* list = (FFlist*) arg->value;
if (list->elementSize != sizeof(FFstrbuf))
{
if (error)
{
FF_STRBUF_AUTO_DESTROY nameA = arg->name ? ffStrbufCreateWS(arg->name) : ffStrbufCreateStatic("(default)");
ffStrbufAppendF(error, "ffRegReadValues(%s) type mismatch: expected list of strbuf for REG_MULTI_SZ", nameA.chars);
}
return false;
}
ffListClear(list);
for (
const wchar_t* ptr = (const wchar_t*) regData;
(const uint8_t*) ptr < (const uint8_t*) regData + regDataLen && *ptr;
ptr++
)
{
uint32_t strLen = (uint32_t) wcsnlen(ptr, regDataLen / sizeof(wchar_t) - (size_t) (ptr - (const wchar_t*) regData));
ffStrbufInitNWS(FF_LIST_ADD(FFstrbuf, *list), strLen, ptr);
ptr += strLen;
}
break;
}
case FF_ARG_TYPE_BUFFER:
{
if (regType != REG_BINARY)
goto type_mismatch;
FFArgBuffer* buffer = (FFArgBuffer*) arg->value;
if (buffer->length == 0)
{
buffer->data = malloc(regDataLen);
}
else if (buffer->length < regDataLen)
{
if (error)
{
FF_STRBUF_AUTO_DESTROY nameA = arg->name ? ffStrbufCreateWS(arg->name) : ffStrbufCreateStatic("(default)");
ffStrbufAppendF(error, "ffRegReadValues(%s) buffer too small (%u): expected %u", nameA.chars, (unsigned) buffer->length, (unsigned) regDataLen);
}
return false;
}
buffer->length = regDataLen;
memcpy(buffer->data, regData, regDataLen);
break;
}
case FF_ARG_TYPE_INT: // Use UINT instead
case FF_ARG_TYPE_STRING:
case FF_ARG_TYPE_NULL:
default:
if (error)
{
FF_STRBUF_AUTO_DESTROY nameA = arg->name ? ffStrbufCreateWS(arg->name) : ffStrbufCreateStatic("(default)");
ffStrbufAppendF(error, "processRegValue(%s) unsupported FFArgType %u", nameA.chars, (unsigned) arg->type);
}
return false;
}
ffStrbufSetNWS(result, (uint32_t) (bufSize / sizeof(*resultW) - 1), resultW);
return true;
type_mismatch:
FF_DEBUG("ffRegReadValues(%ls) type mismatch: regType=%u, argType=%u, dataLen=%u",
arg->name ?: L"(default)", (unsigned) regType, (unsigned) arg->type, (unsigned) regDataLen);
if (error)
{
FF_STRBUF_AUTO_DESTROY nameA = arg->name ? ffStrbufCreateWS(arg->name) : ffStrbufCreateStatic("(default)");
ffStrbufAppendF(error, "ffRegReadValues(%s) type mismatch: regType=%u, argType=%u, dataLen=%u",
nameA.chars, (unsigned) regType, (unsigned) arg->type, (unsigned) regDataLen);
}
return false;
}
bool ffRegReadData(HKEY hKey, const wchar_t* valueNameW, uint8_t** result, uint32_t* length, FFstrbuf* error)
bool ffRegReadValue(HANDLE hKey, const FFRegValueArg* arg, FFstrbuf* error)
{
assert(result && length);
DWORD bufSize = 0;
LONG err = RegGetValueW(hKey, NULL, valueNameW, RRF_RT_REG_BINARY, NULL, NULL, &bufSize);
if(err != ERROR_SUCCESS || bufSize == 0)
{
if(error)
{
if(!valueNameW)
valueNameW = L"(default)";
FF_STRBUF_AUTO_DESTROY valueNameA = ffStrbufCreateWS(valueNameW);
ffStrbufAppendF(error, "RegGetValueW(%s, NULL, RRF_RT_REG_BINARY, NULL, NULL, &bufSize) failed", valueNameA.chars);
}
return false;
}
UNICODE_STRING* valueNameU = &(UNICODE_STRING) {
.Length = arg->name ? (USHORT) (wcslen(arg->name) * sizeof(wchar_t)) : 0 /*(default)*/,
.MaximumLength = 0,
.Buffer = (wchar_t*) arg->name,
};
uint8_t* buf = (uint8_t*) malloc(bufSize);
err = RegGetValueW(hKey, NULL, valueNameW, RRF_RT_REG_BINARY, NULL, buf, &bufSize);
if(err != ERROR_SUCCESS)
{
if(error)
{
if(!valueNameW)
valueNameW = L"(default)";
FF_STRBUF_AUTO_DESTROY valueNameA = ffStrbufCreateWS(valueNameW);
ffStrbufAppendF(error, "RegGetValueW(%s, NULL, RRF_RT_REG_BINARY, NULL, length) failed", valueNameA.chars);
}
free(buf);
return false;
}
*result = buf;
*length = bufSize;
return true;
}
alignas(KEY_VALUE_PARTIAL_INFORMATION) uint8_t staticBuffer[128 + sizeof(KEY_VALUE_PARTIAL_INFORMATION)];
FF_AUTO_FREE uint8_t* dynamicBuffer = NULL;
bool ffRegReadUint(HKEY hKey, const wchar_t* valueNameW, uint32_t* result, FFstrbuf* error)
{
DWORD bufSize = sizeof(*result);
if(RegGetValueW(hKey, NULL, valueNameW, RRF_RT_DWORD, NULL, result, &bufSize) != ERROR_SUCCESS)
{
if(error)
{
if(!valueNameW)
valueNameW = L"(default)";
FF_STRBUF_AUTO_DESTROY valueNameA = ffStrbufCreateWS(valueNameW);
ffStrbufAppendF(error, "RegGetValueW(%s, result, RRF_RT_DWORD) failed", valueNameA.chars);
}
return false;
}
return true;
}
KEY_VALUE_PARTIAL_INFORMATION* buffer = (KEY_VALUE_PARTIAL_INFORMATION*) &staticBuffer;
DWORD bufSize = sizeof(staticBuffer);
if (NT_SUCCESS(NtQueryValueKey(hKey, valueNameU, KeyValuePartialInformation, buffer, bufSize, &bufSize)))
goto process_value;
bool ffRegReadUint64(HKEY hKey, const wchar_t* valueNameW, uint64_t* result, FFstrbuf* error)
{
DWORD bufSize = sizeof(*result);
if(RegGetValueW(hKey, NULL, valueNameW, RRF_RT_QWORD, NULL, result, &bufSize) != ERROR_SUCCESS)
{
if(error)
{
if(!valueNameW)
valueNameW = L"(default)";
FF_STRBUF_AUTO_DESTROY valueNameA = ffStrbufCreateWS(valueNameW);
ffStrbufAppendF(error, "RegGetValueW(%s, result, RRF_RT_QWORD) failed", valueNameA.chars);
}
return false;
}
return true;
}
bool ffRegGetSubKey(HKEY hKey, uint32_t index, FFstrbuf* result, FFstrbuf* error)
{
DWORD bufSize = 0;
if(RegQueryInfoKeyW(hKey, NULL, NULL, NULL, NULL, &bufSize, NULL, NULL, NULL, NULL, NULL, NULL) != ERROR_SUCCESS)
if (bufSize == 0)
{
FF_DEBUG("NtQueryValueKey(%p, %ls) failed (bufSize=0)", hKey, arg->name ?: L"(default)");
if (error)
ffStrbufAppendS(error, "RegQueryInfoKeyW(hKey) failed");
{
FF_STRBUF_AUTO_DESTROY valueNameA = arg->name ? ffStrbufCreateWS(arg->name) : ffStrbufCreateStatic("(default)");
ffStrbufAppendF(error, "NtQueryValueKey(%p, %s) failed", hKey, valueNameA.chars);
}
return false;
}
++bufSize;
wchar_t* FF_AUTO_FREE resultW = (wchar_t*) malloc(bufSize * sizeof(*resultW));
if(RegEnumKeyExW(hKey, index, resultW, &bufSize, NULL, NULL, NULL, NULL) != ERROR_SUCCESS)
dynamicBuffer = (uint8_t*) malloc(bufSize);
buffer = (KEY_VALUE_PARTIAL_INFORMATION*) dynamicBuffer;
if (!NT_SUCCESS(NtQueryValueKey(hKey, valueNameU, KeyValuePartialInformation, buffer, bufSize, &bufSize)))
{
FF_DEBUG("NtQueryValueKey(%p, %ls, buffer=%u) failed", hKey, arg->name ?: L"(default)", (unsigned) bufSize);
if (error)
ffStrbufAppendF(error, "RegEnumKeyExW(hKey, %u) failed", (unsigned) index);
{
FF_STRBUF_AUTO_DESTROY valueNameA = arg->name ? ffStrbufCreateWS(arg->name) : ffStrbufCreateStatic("(default)");
ffStrbufAppendF(error, "NtQueryValueKey(%p, %s, buffer) failed", hKey, valueNameA.chars);
}
return false;
}
ffStrbufSetWS(result, resultW);
return true;
process_value:
FF_DEBUG("Read value from %p (%ls), type=%u, len=%u", hKey, arg->name ?: L"(default)", (unsigned) buffer->Type, (unsigned) buffer->DataLength);
return processRegValue(arg, buffer->Type, buffer->Data, buffer->DataLength, error);
}
bool ffRegGetNSubKeys(HKEY hKey, uint32_t* result, FFstrbuf* error)
bool ffRegReadValues(HANDLE hKey, uint32_t argc, const FFRegValueArg argv[], FFstrbuf* error)
{
DWORD buffer;
if(RegQueryInfoKeyW(hKey, NULL, NULL, NULL, &buffer, NULL, NULL, NULL, NULL, NULL, NULL, NULL) != ERROR_SUCCESS)
if (__builtin_expect(argc == 0, false))
return true;
assert(argv);
FF_AUTO_FREE UNICODE_STRING* names = (UNICODE_STRING*) calloc(argc, sizeof(*names));
FF_AUTO_FREE KEY_VALUE_ENTRY* entries = (KEY_VALUE_ENTRY*) calloc(argc, sizeof(*entries));
for (uint32_t i = 0; i < argc; ++i)
{
if (__builtin_expect(!argv[i].value, false))
{
FF_DEBUG("ffRegReadValues(argv[%u].value) is NULL", (unsigned) i);
if (error) ffStrbufAppendF(error, "ffRegReadValues(argv[%u].pVar) is NULL", (unsigned) i);
return false;
}
names[i] = (UNICODE_STRING) {
.Length = argv[i].name ? (USHORT) (wcslen(argv[i].name) * sizeof(wchar_t)) : 0 /*(default)*/,
.MaximumLength = 0,
.Buffer = (wchar_t*) argv[i].name,
};
entries[i].ValueName = &names[i];
}
ULONG bufferSize = argc * 128;
if (bufferSize < 512)
bufferSize = 512;
FF_AUTO_FREE uint8_t* buffer = NULL;
while (true)
{
buffer = (uint8_t*) realloc(buffer, bufferSize);
ULONG writtenSize = bufferSize;
ULONG requiredSize = 0;
NTSTATUS status = NtQueryMultipleValueKey(hKey, entries, argc, buffer, &writtenSize, &requiredSize);
if (!NT_SUCCESS(status))
{
// Buffer too small: docs guarantee requiredSize is returned when provided.
if (requiredSize > bufferSize)
{
FF_DEBUG("NtQueryMultipleValueKey(%p) resize buffer: %u -> %u", hKey, (unsigned) bufferSize, (unsigned) requiredSize);
bufferSize = requiredSize;
continue;
}
FF_DEBUG("NtQueryMultipleValueKey(%p, argc=%u) failed, status=0x%08X", hKey, (unsigned) argc, (unsigned) status);
if (error)
ffStrbufAppendF(error, "NtQueryMultipleValueKey(%p, argc=%u) failed, status=0x%08X",
hKey, (unsigned) argc, (unsigned) status);
return false;
}
break;
}
for (uint32_t i = 0; i < argc; ++i)
{
const FFRegValueArg* arg = &argv[i];
const KEY_VALUE_ENTRY* entry = &entries[i];
FF_DEBUG("Read value[%u] from %p: type=%u, len=%u", (unsigned) i, hKey, (unsigned) entry->Type, (unsigned) entry->DataLength);
if (!processRegValue(arg, entry->Type, buffer + entry->DataOffset, entry->DataLength, error))
return false;
}
return true;
}
bool ffRegGetSubKey(HANDLE hKey, uint32_t index, FFstrbuf* result, FFstrbuf* error)
{
assert(hKey);
assert(result);
alignas(KEY_BASIC_INFORMATION) uint8_t buffer[sizeof(KEY_BASIC_INFORMATION) + MAX_PATH * sizeof(wchar_t)];
ULONG bufSize = (ULONG) sizeof(buffer);
KEY_BASIC_INFORMATION* keyInfo = (KEY_BASIC_INFORMATION*) buffer;
if (!NT_SUCCESS(NtEnumerateKey(hKey, index, KeyBasicInformation, keyInfo, bufSize, &bufSize)))
{
FF_DEBUG("NtEnumerateKey(hKey=%p, index=%u) failed", hKey, (unsigned) index);
if (error)
ffStrbufAppendS(error, "RegQueryInfoKeyW(hKey) failed");
ffStrbufAppendF(error, "NtEnumerateKey(hKey=%p, %u, keyInfo) failed", hKey, (unsigned) index);
return false;
}
*result = buffer;
ffStrbufSetNWS(result, keyInfo->NameLength / sizeof(wchar_t), keyInfo->Name);
return true;
}
bool ffRegGetNSubKeys(HANDLE hKey, uint32_t* result, FFstrbuf* error)
{
assert(hKey);
assert(result);
alignas(KEY_FULL_INFORMATION) uint8_t buffer[sizeof(KEY_FULL_INFORMATION) + MAX_PATH * sizeof(wchar_t)];
ULONG bufSize = sizeof(buffer);
KEY_FULL_INFORMATION* keyInfo = (KEY_FULL_INFORMATION*) buffer;
if (!NT_SUCCESS(NtQueryKey(hKey, KeyFullInformation, keyInfo, bufSize, &bufSize)))
{
FF_DEBUG("NtQueryKey(hKey=%p, KeyFullInformation) failed", hKey);
if (error)
ffStrbufAppendF(error, "NtQueryKey(hKey=%p, KeyFullInformation, keyInfo) failed", hKey);
return false;
}
*result = (uint32_t) keyInfo->SubKeys;
return true;
}
+60 -13
View File
@@ -1,21 +1,68 @@
#pragma once
#include "fastfetch.h"
#include "common/argType.h"
#include "common/io.h"
#include <windows.h>
#ifndef HKEY_CURRENT_USER
#define HKEY_CLASSES_ROOT ((HKEY) (ULONG_PTR)((LONG)0x80000000))
#define HKEY_CURRENT_USER ((HKEY) (ULONG_PTR)((LONG)0x80000001))
#define HKEY_LOCAL_MACHINE ((HKEY) (ULONG_PTR)((LONG)0x80000002))
#define HKEY_USERS ((HKEY) (ULONG_PTR)((LONG)0x80000003))
#define HKEY_PERFORMANCE_DATA ((HKEY) (ULONG_PTR)((LONG)0x80000004))
#define HKEY_CURRENT_CONFIG ((HKEY) (ULONG_PTR)((LONG)0x80000005))
#define HKEY_DYN_DATA ((HKEY) (ULONG_PTR)((LONG)0x80000006))
#define HKEY_CURRENT_USER_LOCAL_SETTINGS ((HKEY) (ULONG_PTR)((LONG)0x80000007))
#endif
static inline void wrapRegCloseKey(HKEY* phKey)
typedef struct FFRegValueArg
{
if(*phKey)
RegCloseKey(*phKey);
FFArgType type;
const void* value;
const wchar_t* name;
} FFRegValueArg;
HANDLE ffRegGetRootKeyHandle(HKEY hKey);
bool ffRegOpenSubkeyForRead(HANDLE hKey, const wchar_t* subKeyW, HANDLE* result, FFstrbuf* error);
bool ffRegReadValue(HANDLE hKey, const FFRegValueArg* arg, FFstrbuf* error);
bool ffRegReadValues(HANDLE hKey, uint32_t argc, const FFRegValueArg argv[], FFstrbuf* error);
bool ffRegGetSubKey(HANDLE hKey, uint32_t index, FFstrbuf* result, FFstrbuf* error);
bool ffRegGetNSubKeys(HANDLE hKey, uint32_t* result, FFstrbuf* error);
static inline bool ffRegOpenKeyForRead(HKEY hRootKey, const wchar_t* subKeyW, HANDLE* result, FFstrbuf* error)
{
return ffRegOpenSubkeyForRead(ffRegGetRootKeyHandle(hRootKey), subKeyW, result, error);
}
#define FF_HKEY_AUTO_DESTROY HKEY __attribute__((__cleanup__(wrapRegCloseKey)))
bool ffRegOpenKeyForRead(HKEY hKey, const wchar_t* subKeyW, HKEY* result, FFstrbuf* error);
bool ffRegReadStrbuf(HKEY hKey, const wchar_t* valueNameW, FFstrbuf* result, FFstrbuf* error);
bool ffRegReadData(HKEY hKey, const wchar_t* valueNameW, uint8_t** result, uint32_t* length, FFstrbuf* error);
bool ffRegReadUint(HKEY hKey, const wchar_t* valueNameW, uint32_t* result, FFstrbuf* error);
bool ffRegReadUint64(HKEY hKey, const wchar_t* valueNameW, uint64_t* result, FFstrbuf* error);
bool ffRegGetSubKey(HKEY hKey, uint32_t index, FFstrbuf* result, FFstrbuf* error);
bool ffRegGetNSubKeys(HKEY hKey, uint32_t* result, FFstrbuf* error);
static inline bool ffRegReadStrbuf(HANDLE hKey, const wchar_t* valueNameW, FFstrbuf* result, FFstrbuf* error)
{
return ffRegReadValue(hKey, &(FFRegValueArg) {
.type = FF_ARG_TYPE_STRBUF,
.value = result,
.name = valueNameW,
}, error);
}
static inline bool ffRegReadUint(HANDLE hKey, const wchar_t* valueNameW, uint32_t* result, FFstrbuf* error)
{
return ffRegReadValue(hKey, &(FFRegValueArg) {
.type = FF_ARG_TYPE_UINT,
.value = result,
.name = valueNameW,
}, error);
}
static inline bool ffRegReadUint64(HANDLE hKey, const wchar_t* valueNameW, uint64_t* result, FFstrbuf* error)
{
return ffRegReadValue(hKey, &(FFRegValueArg) {
.type = FF_ARG_TYPE_UINT64,
.value = result,
.name = valueNameW,
}, error);
}
static inline bool ffRegReadData(HANDLE hKey, const wchar_t* valueNameW, FFArgBuffer* buffer, FFstrbuf* error)
{
return ffRegReadValue(hKey, &(FFRegValueArg) {
.type = FF_ARG_TYPE_BUFFER,
.value = buffer,
.name = valueNameW,
}, error);
}
+23 -13
View File
@@ -1,6 +1,6 @@
#include "unicode.h"
#include <windows.h>
#include "common/windows/nt.h"
void ffStrbufSetNWS(FFstrbuf* result, uint32_t length, const wchar_t* source)
{
@@ -10,15 +10,19 @@ void ffStrbufSetNWS(FFstrbuf* result, uint32_t length, const wchar_t* source)
return;
}
int size_needed = WideCharToMultiByte(CP_UTF8, 0, source, (int)length, NULL, 0, NULL, NULL);
if (size_needed <= 0)
ULONG size_needed = 0;
NTSTATUS status = RtlUnicodeToUTF8N(NULL, 0, &size_needed, source, length * sizeof(wchar_t));
if (size_needed == 0)
{
ffStrbufSetF(result, "WCTMB failed: %u", (unsigned) GetLastError());
ffStrbufSetF(result, "RtlUnicodeToUTF8N failed: %X", (unsigned) status);
return;
}
ffStrbufEnsureFixedLengthFree(result, (uint32_t)size_needed);
WideCharToMultiByte(CP_UTF8, 0, source, (int)length, result->chars, size_needed, NULL, NULL);
result->length = (uint32_t)size_needed;
ffStrbufEnsureFixedLengthFree(result, size_needed);
RtlUnicodeToUTF8N(result->chars, size_needed, &size_needed, source, length * sizeof(wchar_t));
result->length = size_needed;
result->chars[size_needed] = '\0';
}
@@ -27,12 +31,18 @@ void ffStrbufAppendNWS(FFstrbuf* result, uint32_t length, const wchar_t* source)
if(!length)
return;
int size_needed = WideCharToMultiByte(CP_UTF8, 0, source, (int)length, NULL, 0, NULL, NULL);
if (size_needed <= 0)
return;
ULONG size_needed = 0;
NTSTATUS status = RtlUnicodeToUTF8N(NULL, 0, &size_needed, source, length * sizeof(wchar_t));
ffStrbufEnsureFree(result, (uint32_t)size_needed);
WideCharToMultiByte(CP_UTF8, 0, source, (int)length, result->chars + result->length, size_needed, NULL, NULL);
result->length += (uint32_t)size_needed;
if (size_needed == 0)
{
ffStrbufAppendF(result, "RtlUnicodeToUTF8N failed: %X", (unsigned) status);
return;
}
ffStrbufEnsureFree(result, size_needed);
RtlUnicodeToUTF8N(result->chars + result->length, size_needed, &size_needed, source, length * sizeof(wchar_t));
result->length += size_needed;
result->chars[result->length] = '\0';
}
+5
View File
@@ -13,6 +13,11 @@ static inline void ffStrbufInitWSV(FFstrbuf* result, const std::wstring_view sou
return ffStrbufInitNWS(result, (uint32_t) source.size(), source.data());
}
static inline FFstrbuf ffStrbufCreateWSV(const std::wstring_view source)
{
return ffStrbufCreateNWS((uint32_t) source.size(), source.data());
}
static inline void ffStrbufSetWSV(FFstrbuf* result, const std::wstring_view source)
{
return ffStrbufSetNWS(result, (uint32_t) source.size(), source.data());
+21
View File
@@ -0,0 +1,21 @@
#include "variant.hpp"
#include <oleauto.h>
FFWmiVariant::FFWmiVariant(std::initializer_list<PCWSTR> 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;
}
+18
View File
@@ -3,6 +3,8 @@
#include <type_traits>
#include <utility>
#include <string_view>
#include <cassert>
#include <cstdint>
template <typename TVariant>
struct FFBaseVariant: TVariant
@@ -137,6 +139,7 @@ struct FFWmiVariant: FFBaseVariant<VARIANT>
FFWmiVariant(const FFWmiVariant&) = delete;
FFWmiVariant(FFWmiVariant&&); // don't define it to enforce NRVO optimization
explicit FFWmiVariant() { VariantInit(this); }
explicit FFWmiVariant(std::initializer_list<PCWSTR> strings);
~FFWmiVariant() { VariantClear(this); }
};
static_assert(sizeof(FFWmiVariant) == sizeof(VARIANT), "");
@@ -149,3 +152,18 @@ struct FFPropVariant: FFBaseVariant<PROPVARIANT>
~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;
};
}
+78 -48
View File
@@ -1,62 +1,92 @@
#include "common/windows/version.h"
#include "common/debug.h"
#include "common/mallocHelper.h"
#include "common/windows/version.h"
#include "common/windows/unicode.h"
#include <windows.h>
bool ffGetFileVersion(const wchar_t* filePath, const wchar_t* stringName, FFstrbuf* version)
{
FF_DEBUG("ffGetFileVersion: enter filePath=%ls stringName=%ls", filePath, stringName);
DWORD handle;
DWORD size = GetFileVersionInfoSizeW(filePath, &handle);
if (size > 0)
if (size == 0)
{
FF_AUTO_FREE void *versionData = malloc(size);
if (GetFileVersionInfoW(filePath, handle, size, versionData))
{
if (!stringName)
{
VS_FIXEDFILEINFO* verInfo;
UINT len;
if (VerQueryValueW(versionData, L"\\", (void **)&verInfo, &len) && len && verInfo->dwSignature == 0xFEEF04BD)
{
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));
return true;
}
}
else
{
struct
{
WORD language;
WORD codePage;
}* translations;
UINT translationsLen;
if (VerQueryValueW(versionData, L"\\VarFileInfo\\Translation",
(void **) &translations, &translationsLen) &&
translationsLen >= sizeof(*translations))
{
wchar_t subBlock[128];
snwprintf(subBlock, ARRAY_SIZE(subBlock), L"\\StringFileInfo\\%04x%04x\\%ls",
translations[0].language, translations[0].codePage, stringName);
wchar_t* value;
UINT valueLen;
if (VerQueryValueW(versionData, subBlock, (void **)&value, &valueLen) && valueLen > 0)
{
ffStrbufSetWS(version, value);
return true;
}
}
}
}
DWORD err = GetLastError();
FF_DEBUG("GetFileVersionInfoSizeW failed: err=%lu (%s)",
(unsigned long) err, ffDebugWin32Error(err));
return false;
}
FF_DEBUG("GetFileVersionInfoSizeW ok: size=%lu handle=%lu",
(unsigned long) size, (unsigned long) handle);
FF_AUTO_FREE void* versionData = malloc(size);
if (!versionData)
{
FF_DEBUG("malloc failed: size=%lu", (unsigned long) size);
return false;
}
if (!GetFileVersionInfoW(filePath, handle, size, versionData))
{
DWORD err = GetLastError();
FF_DEBUG("GetFileVersionInfoW failed: err=%lu (%s)",
(unsigned long) err, ffDebugWin32Error(err));
return false;
}
FF_DEBUG("GetFileVersionInfoW ok");
if (!stringName)
{
VS_FIXEDFILEINFO* verInfo;
UINT len;
if (VerQueryValueW(versionData, L"\\", (void**) &verInfo, &len) &&
len &&
verInfo->dwSignature == 0xFEEF04BD)
{
ffStrbufSetF(version, "%u.%u.%u.%u",
(unsigned) ((verInfo->dwProductVersionMS >> 16) & 0xffff),
(unsigned) ((verInfo->dwProductVersionMS >> 0) & 0xffff),
(unsigned) ((verInfo->dwProductVersionLS >> 16) & 0xffff),
(unsigned) ((verInfo->dwProductVersionLS >> 0) & 0xffff));
FF_DEBUG("fixed version resolved: %s", version->chars);
return true;
}
FF_DEBUG("fixed version query failed or invalid signature");
return false;
}
struct { WORD language; WORD codePage; }* translations;
UINT translationsLen;
if (VerQueryValueW(versionData, L"\\VarFileInfo\\Translation", (void**) &translations, &translationsLen) &&
translationsLen >= sizeof(*translations))
{
wchar_t subBlock[128];
snwprintf(subBlock, ARRAY_SIZE(subBlock), L"\\StringFileInfo\\%04x%04x\\%ls",
translations[0].language, translations[0].codePage, stringName);
FF_DEBUG("query version string with translation: %ls", subBlock);
wchar_t* value;
UINT valueLen; // Number of characters, including null terminator
if (VerQueryValueW(versionData, subBlock, (void**) &value, &valueLen) && valueLen > 0)
{
ffStrbufSetNWS(version, valueLen - 1, value);
FF_DEBUG("version string resolved (translation fallback): %s", version->chars);
return true;
}
FF_DEBUG("translation fallback query failed");
}
else
{
FF_DEBUG("no translation table found in version resource");
}
FF_DEBUG("ffGetFileVersion failed");
return false;
}
-16
View File
@@ -8,7 +8,6 @@ extern "C" {
#include <initguid.h>
#include <wbemidl.h>
#include <cassert>
#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
-1
View File
@@ -11,7 +11,6 @@
#include <batclass.h>
#include <devguid.h>
#include <cfgmgr32.h>
#include <winternl.h>
static const char* detectWithCmApi(FFBatteryOptions* options, FFlist* results)
{
+7 -9
View File
@@ -29,20 +29,18 @@ const char* ffDetectBios(FFBiosResult* bios)
if (!device)
return "IODeviceTree:/ not found";
FF_CFTYPE_AUTO_RELEASE CFMutableDictionaryRef deviceProps = NULL;
if(IORegistryEntryCreateCFProperties(device, &deviceProps, kCFAllocatorDefault, kNilOptions) != kIOReturnSuccess)
return "IORegistryEntryCreateCFProperties(device) failed";
ffCfDictGetString(deviceProps, CFSTR("manufacturer"), &bios->vendor);
ffCfDictGetString(deviceProps, CFSTR("time-stamp"), &bios->date);
FF_CFTYPE_AUTO_RELEASE CFDataRef manufacturer = IORegistryEntryCreateCFProperty(device, CFSTR("manufacturer"), kCFAllocatorDefault, kNilOptions);
ffCfStrGetString(manufacturer, &bios->vendor);
FF_CFTYPE_AUTO_RELEASE CFDataRef timeStamp = IORegistryEntryCreateCFProperty(device, CFSTR("time-stamp"), kCFAllocatorDefault, kNilOptions);
ffCfStrGetString(timeStamp, &bios->date);
FF_IOOBJECT_AUTO_RELEASE io_registry_entry_t deviceChosen = IORegistryEntryFromPath(MACH_PORT_NULL, "IODeviceTree:/chosen");
if (deviceChosen)
{
FF_CFTYPE_AUTO_RELEASE CFStringRef systemFirmWareVersion = IORegistryEntryCreateCFProperty(deviceChosen, CFSTR("system-firmware-version"), kCFAllocatorDefault, kNilOptions);
if (systemFirmWareVersion)
FF_CFTYPE_AUTO_RELEASE CFStringRef systemFirmwareVersion = IORegistryEntryCreateCFProperty(deviceChosen, CFSTR("system-firmware-version"), kCFAllocatorDefault, kNilOptions);
if (systemFirmwareVersion)
{
ffCfStrGetString(systemFirmWareVersion, &bios->version);
ffCfStrGetString(systemFirmwareVersion, &bios->version);
uint32_t index = ffStrbufFirstIndexC(&bios->version, '-');
if (index != bios->version.length)
{
+12 -6
View File
@@ -2,14 +2,20 @@
#include "common/io.h"
#include "common/smbiosHelper.h"
#include <stdlib.h>
const char* ffDetectBios(FFBiosResult* bios)
{
ffGetSmbiosValue("/sys/devices/virtual/dmi/id/bios_date", "/sys/class/dmi/id/bios_date", &bios->date);
ffGetSmbiosValue("/sys/devices/virtual/dmi/id/bios_release", "/sys/class/dmi/id/bios_release", &bios->release);
ffGetSmbiosValue("/sys/devices/virtual/dmi/id/bios_vendor", "/sys/class/dmi/id/bios_vendor", &bios->vendor);
ffGetSmbiosValue("/sys/devices/virtual/dmi/id/bios_version", "/sys/class/dmi/id/bios_version", &bios->version);
if (ffGetSmbiosValue("/sys/devices/virtual/dmi/id/bios_date", "/sys/class/dmi/id/bios_date", &bios->date))
{
ffGetSmbiosValue("/sys/devices/virtual/dmi/id/bios_release", "/sys/class/dmi/id/bios_release", &bios->release);
ffGetSmbiosValue("/sys/devices/virtual/dmi/id/bios_vendor", "/sys/class/dmi/id/bios_vendor", &bios->vendor);
ffGetSmbiosValue("/sys/devices/virtual/dmi/id/bios_version", "/sys/class/dmi/id/bios_version", &bios->version);
}
else if (ffReadFileBuffer("/proc/device-tree/chosen/u-boot,version", &bios->version))
{
ffStrbufTrimRight(&bios->version, '\0');
ffStrbufSetStatic(&bios->vendor, "U-Boot");
}
if (ffPathExists("/sys/firmware/efi/", FF_PATHTYPE_DIRECTORY) || ffPathExists("/sys/firmware/acpi/tables/UEFI", FF_PATHTYPE_FILE))
ffStrbufSetStatic(&bios->type, "UEFI");
else
+10 -25
View File
@@ -5,37 +5,18 @@
#include "common/windows/registry.h"
#include <ntstatus.h>
#include <winternl.h>
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"
#include <fcntl.h>
#include <unistd.h>
#elif __sun
#include <libdevinfo.h>
#include <sys/sunddi.h>
#elif __APPLE__
#include "common/apple/cf_helpers.h"
#include <IOKit/IOKitLib.h>
#endif
typedef struct FFSmbiosBios
@@ -90,7 +71,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)
{
@@ -111,8 +92,12 @@ const char* ffDetectBios(FFBiosResult* bios)
}
di_fini(rootNode);
#elif __HAIKU__ || __OpenBSD__
// Currently SMBIOS detection is supported in legancy BIOS only
// Currently SMBIOS detection is supported in legacy BIOS only
ffStrbufSetStatic(&bios->type, "BIOS");
#elif __APPLE__
// Intel Macs use UEFI from day one
FF_IOOBJECT_AUTO_RELEASE io_registry_entry_t deviceEfi = IORegistryEntryFromPath(MACH_PORT_NULL, "IODeviceTree:/efi");
ffStrbufSetStatic(&bios->type, deviceEfi ? "UEFI" : "BIOS");
#endif
return NULL;
+1 -1
View File
@@ -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;
+1 -2
View File
@@ -9,8 +9,7 @@
const char* ffDetectBluetooth(FFBluetoothOptions* options, FFlist* devices /* FFBluetoothResult */)
{
// Actually bluetoothapis.dll, but it's missing on Windows 7
FF_LIBRARY_LOAD_MESSAGE(bluetoothapis, "bthprops.cpl", 1)
FF_LIBRARY_LOAD_MESSAGE(bluetoothapis, "bluetoothapis.dll", 1)
FF_LIBRARY_LOAD_SYMBOL_MESSAGE(bluetoothapis, BluetoothFindFirstDevice)
FF_LIBRARY_LOAD_SYMBOL_MESSAGE(bluetoothapis, BluetoothFindNextDevice)
FF_LIBRARY_LOAD_SYMBOL_MESSAGE(bluetoothapis, BluetoothFindDeviceClose)
+2 -11
View File
@@ -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";
}
@@ -79,8 +71,7 @@ const char* ffBluetoothDetectBattery(FFlist* devices)
batt = data.get<uint8_t>();
else
{
FF_STRBUF_AUTO_DESTROY addr; // MAC address without colon
ffStrbufInitWSV(&addr, data.get<std::wstring_view>());
FF_STRBUF_AUTO_DESTROY addr = ffStrbufCreateWSV(data.get<std::wstring_view>()); // MAC address without colon
if (__builtin_expect(addr.length != 12, 0))
continue;
@@ -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;
@@ -54,7 +54,7 @@ static_assert(sizeof(BTH_LOCAL_RADIO_INFO) == 292, "BTH_LOCAL_RADIO_INFO should
const char* ffDetectBluetoothRadio(FFlist* devices /* FFBluetoothRadioResult */)
{
// Actually bluetoothapis.dll, but it's missing on Windows 7
FF_LIBRARY_LOAD_MESSAGE(bluetoothapis, "bthprops.cpl", 1)
FF_LIBRARY_LOAD_MESSAGE(bluetoothapis, "bluetoothapis.dll", 1)
FF_LIBRARY_LOAD_SYMBOL_MESSAGE(bluetoothapis, BluetoothFindFirstRadio)
FF_LIBRARY_LOAD_SYMBOL_MESSAGE(bluetoothapis, BluetoothFindNextRadio)
FF_LIBRARY_LOAD_SYMBOL_MESSAGE(bluetoothapis, BluetoothFindRadioClose)
@@ -98,7 +98,7 @@ const char* ffDetectBluetoothRadio(FFlist* devices /* FFBluetoothRadioResult */)
device->connectable = ffBluetoothIsConnectable(hRadio);
device->discoverable = ffBluetoothIsDiscoverable(hRadio);
CloseHandle(hRadio);
NtClose(hRadio);
} while (ffBluetoothFindNextRadio(hFind, &hRadio));
ffBluetoothFindRadioClose(hFind);
+2 -1
View File
@@ -9,6 +9,7 @@
#endif
#include <sys/ioctl.h>
#include <fcntl.h>
#include <stdalign.h>
#ifdef __NetBSD__
typedef uint16_t efi_char;
@@ -23,7 +24,7 @@ const char* ffDetectBootmgr(FFBootmgrResult* result)
FF_AUTO_CLOSE_FD int efifd = open("/dev/efi", O_RDWR | O_CLOEXEC);
if (efifd < 0) return "open(/dev/efi) failed";
uint8_t buffer[2048];
alignas(uint16_t) uint8_t buffer[2048];
struct efi_var_ioc ioc = {
.vendor = EFI_GLOBAL_VARIABLE,
.data = buffer,
+3 -1
View File
@@ -2,11 +2,13 @@
#include "common/io.h"
#include "efi_helper.h"
#include <stdalign.h>
#define FF_EFIVARS_PATH_PREFIX "/sys/firmware/efi/efivars/"
const char* ffDetectBootmgr(FFBootmgrResult* result)
{
uint8_t buffer[2048];
alignas(uint16_t) uint8_t buffer[2048];
if (ffReadFileData(FF_EFIVARS_PATH_PREFIX "BootCurrent-" FF_EFI_GLOBAL_GUID, sizeof(buffer), buffer) != 6)
return "Failed to read efivar: BootCurrent";
+29 -16
View File
@@ -1,14 +1,16 @@
#include "bootmgr.h"
#include "efi_helper.h"
#include "common/io.h"
#include "common/windows/nt.h"
#include <ntstatus.h>
#include <windows.h>
const char* enablePrivilege(const wchar_t* privilege)
{
FF_AUTO_CLOSE_FD HANDLE token = NULL;
if (!OpenProcessToken(GetCurrentProcess(), TOKEN_ADJUST_PRIVILEGES | TOKEN_QUERY, &token))
return "OpenProcessToken() failed";
if (!NT_SUCCESS(NtOpenProcessToken(NtCurrentProcess(), TOKEN_ADJUST_PRIVILEGES, &token)))
return "NtOpenProcessToken() failed";
TOKEN_PRIVILEGES tp = {
.PrivilegeCount = 1,
@@ -19,35 +21,46 @@ const char* enablePrivilege(const wchar_t* privilege)
if (!LookupPrivilegeValueW(NULL, privilege, &tp.Privileges[0].Luid))
return "LookupPrivilegeValue() failed";
if (!AdjustTokenPrivileges(token, false, &tp, sizeof(tp), NULL, NULL))
return "AdjustTokenPrivileges() failed";
NTSTATUS status = NtAdjustPrivilegesToken(token, false, &tp, sizeof(tp), NULL, NULL);
if (!NT_SUCCESS(status))
return "NtAdjustPrivilegesToken() failed";
if (GetLastError() == ERROR_NOT_ALL_ASSIGNED)
return "The token does not have the specified privilege";
if (status == STATUS_NOT_ALL_ASSIGNED)
return "The token does not have the specified privilege; try sudo please";
return NULL;
}
const char* ffDetectBootmgr(FFBootmgrResult* result)
{
if (enablePrivilege(L"SeSystemEnvironmentPrivilege") != NULL)
return "Failed to enable SeSystemEnvironmentPrivilege";
const char* err = enablePrivilege(L"SeSystemEnvironmentPrivilege");
if (err != NULL)
return err;
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;
}
+9 -6
View File
@@ -110,26 +110,29 @@ static const char* detectWithDdcci(FF_MAYBE_UNUSED FFBrightnessOptions* options,
FF_LIBRARY_LOAD_SYMBOL_MESSAGE(libddcutil, ddca_close_display)
#ifndef FF_DISABLE_DLOPEN
__typeof__(&ddca_init) ffddca_init = dlsym(libddcutil, "ddca_init");
FF_LIBRARY_LOAD_SYMBOL_LAZY(libddcutil, ddca_init)
if (ffddca_init)
#else
__typeof__(&ddca_init) ffddca_init = ddca_init;
#endif
{
FF_SUPPRESS_IO();
// Ref: https://github.com/rockowitz/ddcutil/issues/344
if (ffddca_init(NULL, -1 /*DDCA_SYSLOG_NOT_SET*/, 1 /*DDCA_INIT_OPTIONS_DISABLE_CONFIG_FILE*/) < 0)
return "ddca_init() failed";
}
#ifndef FF_DISABLE_DLOPEN
else
{
__typeof__(&ddca_set_default_sleep_multiplier) ffddca_set_default_sleep_multiplier = dlsym(libddcutil, "ddca_set_default_sleep_multiplier");
FF_LIBRARY_LOAD_SYMBOL_LAZY(libddcutil, ddca_set_default_sleep_multiplier);
if (ffddca_set_default_sleep_multiplier)
ffddca_set_default_sleep_multiplier(options->ddcciSleep / 40.0);
libddcutil = NULL; // Don't dlclose libddcutil. See https://github.com/rockowitz/ddcutil/issues/330
}
#else
#if DDCUTIL_VMAJOR >= 2
if (ddca_init(NULL, -1 /*DDCA_SYSLOG_NOT_SET*/, 1 /*DDCA_INIT_OPTIONS_DISABLE_CONFIG_FILE*/) < 0)
return "ddca_init() failed";
#else
ddca_set_default_sleep_multiplier(options->ddcciSleep / 40.0);
#endif
#endif
FF_AUTO_FREE DDCA_Display_Info_List* infoList = NULL;
+67 -16
View File
@@ -7,8 +7,37 @@ extern "C"
#include "common/windows/wmi.hpp"
#include "common/windows/unicode.hpp"
#include <highlevelmonitorconfigurationapi.h>
#include <physicalmonitorenumerationapi.h>
#include <winternl.h>
NTSYSAPI NTSTATUS WINAPI GetPhysicalMonitors(
_In_ UNICODE_STRING *pstrDeviceName,
_In_ DWORD dwPhysicalMonitorArraySize,
_Out_ DWORD *pdwNumPhysicalMonitorHandlesInArray,
_Out_ HANDLE *phPhysicalMonitorArray
);
typedef enum _MC_VCP_CODE_TYPE {
MC_MOMENTARY,
MC_SET_PARAMETER
} MC_VCP_CODE_TYPE, *LPMC_VCP_CODE_TYPE;
NTSYSAPI NTSTATUS WINAPI DDCCIGetVCPFeature(
_In_ HANDLE hMonitor,
_In_ DWORD dwVCPCode,
_Out_opt_ LPMC_VCP_CODE_TYPE pvct,
_Out_ DWORD *pdwCurrentValue,
_Out_opt_ DWORD *pdwMaximumValue
);
NTSYSAPI NTSTATUS WINAPI DestroyPhysicalMonitorInternal(
_In_ HANDLE hMonitor
);
NTSTATUS WINAPI GetPhysicalMonitorDescription(
_In_ HANDLE hMonitor,
_In_ DWORD dwPhysicalMonitorDescriptionSizeInChars,
_Out_ LPWSTR szPhysicalMonitorDescription
);
static const char* detectWithWmi(FFlist* result)
{
@@ -40,33 +69,55 @@ static const char* detectWithWmi(FFlist* result)
static const char* detectWithDdcci(const FFDisplayServerResult* displayServer, FFlist* result)
{
FF_LIBRARY_LOAD_MESSAGE(dxva2, "dxva2" FF_LIBRARY_EXTENSION, 1)
FF_LIBRARY_LOAD_SYMBOL_MESSAGE(dxva2, GetPhysicalMonitorsFromHMONITOR)
FF_LIBRARY_LOAD_SYMBOL_MESSAGE(dxva2, GetMonitorBrightness)
FF_LIBRARY_LOAD_SYMBOL_MESSAGE(dxva2, DestroyPhysicalMonitor)
void* gdi32 = ffLibraryGetModule(L"gdi32.dll");
if (!gdi32) return "ffLibraryGetModule(gdi32.dll) failed";
FF_LIBRARY_LOAD_SYMBOL_MESSAGE(gdi32, GetPhysicalMonitors)
FF_LIBRARY_LOAD_SYMBOL_MESSAGE(gdi32, DDCCIGetVCPFeature)
FF_LIBRARY_LOAD_SYMBOL_MESSAGE(gdi32, DestroyPhysicalMonitorInternal)
FF_LIST_FOR_EACH(FFDisplayResult, display, displayServer->displays)
{
PHYSICAL_MONITOR physicalMonitor;
if (ffGetPhysicalMonitorsFromHMONITOR((HMONITOR)(uintptr_t) display->id, 1, &physicalMonitor))
if (display->type == FF_DISPLAY_TYPE_BUILTIN) continue;
MONITORINFOEXW mi;
mi.cbSize = sizeof(mi);
if (!GetMonitorInfoW((HMONITOR)(uintptr_t) display->id, (LPMONITORINFO) &mi))
continue;
UNICODE_STRING deviceName = {
.Length = (USHORT) (wcslen(mi.szDevice) * sizeof(wchar_t)),
.MaximumLength = 0,
.Buffer = mi.szDevice,
};
HANDLE physicalMonitor;
DWORD monitorCount = 0;
if (NT_SUCCESS(ffGetPhysicalMonitors(&deviceName, 1, &monitorCount, &physicalMonitor)) && monitorCount >= 1)
{
DWORD min = 0, curr = 0, max = 0;
if (ffGetMonitorBrightness(physicalMonitor.hPhysicalMonitor, &min, &curr, &max))
DWORD curr = 0, max = 0;
if (NT_SUCCESS(ffDDCCIGetVCPFeature(physicalMonitor, 0x10 /* luminance */, NULL, &curr, &max)))
{
FFBrightnessResult* brightness = (FFBrightnessResult*) ffListAdd(result);
if (display->name.length)
if (display->name.length > 0)
ffStrbufInitCopy(&brightness->name, &display->name);
else
ffStrbufInitWS(&brightness->name, physicalMonitor.szPhysicalMonitorDescription);
{
FF_LIBRARY_LOAD_SYMBOL_LAZY(gdi32, GetPhysicalMonitorDescription)
if (ffGetPhysicalMonitorDescription)
{
wchar_t description[128 /*MUST be PHYSICAL_MONITOR_DESCRIPTION_SIZE*/];
if (NT_SUCCESS(ffGetPhysicalMonitorDescription(physicalMonitor, ARRAY_SIZE(description), description)))
ffStrbufInitWS(&brightness->name, description);
}
if (brightness->name.length == 0)
ffStrbufSetNWS(&brightness->name, deviceName.Length / 2, deviceName.Buffer);
}
brightness->max = max;
brightness->min = min;
brightness->min = 0;
brightness->current = curr;
brightness->builtin = false;
}
ffDestroyPhysicalMonitor(physicalMonitor.hPhysicalMonitor);
ffDestroyPhysicalMonitorInternal(physicalMonitor);
}
}
return NULL;
+4 -2
View File
@@ -46,7 +46,7 @@ static const char* armPartId2name(uint32_t partId)
case 0xa20: return "ARM1020";
case 0xa22: return "ARM1022";
case 0xa26: return "ARM1026";
case 0xb02: return "ARM11 MPCore";
case 0xb02: return "ARM11-MPCore";
case 0xb36: return "ARM1136";
case 0xb56: return "ARM1156";
case 0xb76: return "ARM1176";
@@ -82,10 +82,12 @@ static const char* armPartId2name(uint32_t partId)
case 0xd0d: return "Cortex-A77";
case 0xd0e: return "Cortex-A76AE";
case 0xd13: return "Cortex-R52";
case 0xd14: return "Cortex-R82AE";
case 0xd15: return "Cortex-R82";
case 0xd16: return "Cortex-R52+";
case 0xd20: return "Cortex-M23";
case 0xd21: return "Cortex-M33";
case 0xd24: return "Cortex-M52";
case 0xd22: return "Cortex-M55";
case 0xd23: return "Cortex-M85";
case 0xd40: return "Neoverse-V1";
@@ -215,7 +217,7 @@ static const char* nvidiaPartId2name(uint32_t partId)
switch (partId)
{
case 0x000: return "Denver";
case 0x003: return "Denver 2";
case 0x003: return "Denver-2";
case 0x004: return "Carmel";
case 0x010: return "Olympus";
default: return NULL;
+14 -24
View File
@@ -6,6 +6,7 @@
#include <windows.h>
#include "common/windows/perflib_.h"
#include "common/windows/nt.h"
#include <wchar.h>
static inline void ffPerfCloseQueryHandle(HANDLE* phQuery)
@@ -37,10 +38,8 @@ const char* detectThermalTemp(const FFCPUOptions* options, double* result)
if (options->tempSensor.length > 0)
{
int written = MultiByteToWideChar(CP_UTF8, MB_ERR_INVALID_CHARS, options->tempSensor.chars, (int) options->tempSensor.length, querySpec.Name, (int)(ARRAY_SIZE(querySpec.Name) - 1));
if (written == 0)
if (!NT_SUCCESS(RtlUTF8ToUnicodeN(querySpec.Name, (ULONG) sizeof(querySpec.Name), NULL, options->tempSensor.chars, (ULONG)options->tempSensor.length + 1)))
return "Invalid temp sensor string";
querySpec.Name[written] = L'\0';
}
DWORD dataSize = 0;
@@ -212,15 +211,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(
@@ -250,28 +250,18 @@ static const char* detectNCores(FFCPUResult* cpu)
static const char* detectByRegistry(FFCPUResult* cpu)
{
FF_HKEY_AUTO_DESTROY hKey = NULL;
FF_AUTO_CLOSE_FD HANDLE hKey = NULL;
if(!ffRegOpenKeyForRead(HKEY_LOCAL_MACHINE, L"HARDWARE\\DESCRIPTION\\System\\CentralProcessor\\0", &hKey, NULL))
return "ffRegOpenKeyForRead(HKEY_LOCAL_MACHINE, L\"HARDWARE\\DESCRIPTION\\System\\CentralProcessor\\0\", &hKey, NULL) failed";
ffRegReadStrbuf(hKey, L"ProcessorNameString", &cpu->name, NULL);
if (ffRegReadStrbuf(hKey, L"VendorIdentifier", &cpu->vendor, NULL))
if (ffRegReadValues(hKey, 3, (FFRegValueArg[]) {
FF_ARG(cpu->name, L"ProcessorNameString"),
FF_ARG(cpu->vendor, L"VendorIdentifier"),
FF_ARG(cpu->frequencyBase, L"~MHz"),
}, NULL))
ffStrbufTrimRightSpace(&cpu->vendor);
if (cpu->coresLogical == 0)
{
FF_HKEY_AUTO_DESTROY hProcsKey = NULL;
if (ffRegOpenKeyForRead(HKEY_LOCAL_MACHINE, L"HARDWARE\\DESCRIPTION\\System\\CentralProcessor", &hProcsKey, NULL))
{
uint32_t cores;
if (ffRegGetNSubKeys(hProcsKey, &cores, NULL))
cpu->coresOnline = cpu->coresPhysical = cpu->coresLogical = (uint16_t) cores;
}
}
uint32_t mhz;
if(ffRegReadUint(hKey, L"~MHz", &mhz, NULL))
cpu->frequencyBase = mhz;
else
return "ffRegReadValues() failed for CPU registry key";
return NULL;
}
+6 -3
View File
@@ -44,12 +44,15 @@ static const char* parseCpuCacheIndex(FFstrbuf* path, FFCPUCacheResult* result,
ffStrbufSubstrBefore(path, baseLen);
ffStrbufAppendS(path, "/shared_cpu_list");
if (!ffReadFileBuffer(path->chars, buffer))
return "ffReadFileBuffer(\"/sys/devices/system/cpu/cpuX/cache/indexX/shared_cpu_list\") == NULL";
ffStrbufClear(buffer);
ffStrbufAppendC(buffer, '[');
if (!ffAppendFileBuffer(path->chars, buffer))
return "ffAppendFileBuffer(\"/sys/devices/system/cpu/cpuX/cache/indexX/shared_cpu_list\") == NULL";
ffStrbufTrimRightSpace(buffer);
// deduplicate shared caches
ffStrbufAppendF(buffer, "_%u_%u_%u_%u\n", level, sizeKb, lineSize, cacheType);
ffStrbufAppendF(buffer, "_%u_%u_%u_%u]", level, sizeKb, lineSize, cacheType);
if (ffStrbufContain(added, buffer)) return NULL;
ffStrbufAppend(added, buffer);
ffCPUCacheAddItem(result, level, sizeKb * 1024, lineSize, cacheType);
+5 -2
View File
@@ -37,9 +37,12 @@ const char* ffDetectCPUCache(FFCPUCacheResult* result)
if (!data)
return "Cache information is not found in SMBIOS data";
for (; data->Header.Type == FF_SMBIOS_TYPE_CACHE_INFO;
data = (const FFSmbiosCacheInfo*) ffSmbiosNextEntry(&data->Header))
const FFSmbiosCacheInfo* endOfTable = (const FFSmbiosCacheInfo*) (*smbiosTable)[FF_SMBIOS_TYPE_END_OF_TABLE];
for (; data != endOfTable; data = (const FFSmbiosCacheInfo*) ffSmbiosNextEntry(&data->Header))
{
if (data->Header.Type != FF_SMBIOS_TYPE_CACHE_INFO)
continue;
bool enabled = !!(data->CacheConfiguration & (1 << 7));
if (!enabled)
continue;
+4 -4
View File
@@ -1,19 +1,19 @@
#include "cpucache.h"
#include "common/mallocHelper.h"
#include <windows.h>
#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(
+10 -14
View File
@@ -1,13 +1,12 @@
#include "fastfetch.h"
#include "detection/cpuusage/cpuusage.h"
#include "common/mallocHelper.h"
#include "common/debug.h"
#include <ntstatus.h>
#include <winternl.h>
#include <windows.h>
#include <wchar.h>
#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
const char* error = NULL;
if (winver >= 10)
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;
}
+7 -6
View File
@@ -1,17 +1,18 @@
#include "cursor.h"
#include "common/io.h"
#include "common/windows/registry.h"
void ffDetectCursor(FFCursorResult* result)
{
FF_HKEY_AUTO_DESTROY hKey;
FF_AUTO_CLOSE_FD HANDLE hKey = NULL;
if(ffRegOpenKeyForRead(HKEY_CURRENT_USER, L"Control Panel\\Cursors", &hKey, &result->error))
{
if (!ffRegReadStrbuf(hKey, NULL, &result->theme, &result->error))
return;
uint32_t cursorBaseSize;
if (ffRegReadUint(hKey, L"CursorBaseSize", &cursorBaseSize, NULL))
ffStrbufAppendF(&result->size, "%u", (unsigned) cursorBaseSize);
if (ffRegReadValues(hKey, 2, (FFRegValueArg[]) {
FF_ARG(result->theme, NULL),
FF_ARG(cursorBaseSize, L"CursorBaseSize"),
}, &result->error))
ffStrbufAppendUInt(&result->size, cursorBaseSize);
}
}
+1 -1
View File
@@ -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";
+1 -15
View File
@@ -140,21 +140,7 @@ static void detectName(FFDisk* disk)
if (disk->name.length == 0) return;
// Basic\x20data\x20partition
for (uint32_t i = ffStrbufFirstIndexS(&disk->name, "\\x");
i != disk->name.length;
i = ffStrbufNextIndexS(&disk->name, i + 1, "\\x"))
{
uint32_t len = (uint32_t) strlen("\\x20");
if (disk->name.length >= len)
{
char bak = disk->name.chars[i + len];
disk->name.chars[i + len] = '\0';
disk->name.chars[i] = (char) strtoul(&disk->name.chars[i + 2], NULL, 16);
ffStrbufRemoveSubstr(&disk->name, i + 1, i + len);
disk->name.chars[i + 1] = bak;
}
}
ffStrbufDecodeHexEscapeSequences(&disk->name);
}
#ifdef __ANDROID__

Some files were not shown because too many files have changed in this diff Show More