diff --git a/.github/ISSUE_TEMPLATE/bug_report.md b/.github/ISSUE_TEMPLATE/bug_report.md index be682e7b0..f955b1113 100644 --- a/.github/ISSUE_TEMPLATE/bug_report.md +++ b/.github/ISSUE_TEMPLATE/bug_report.md @@ -7,6 +7,8 @@ assignees: '' --- +Be sure to read [FAQ](https://github.com/fastfetch-cli/fastfetch?tab=readme-ov-file#faq) before submitting a new issue. + # General description of bug: @@ -74,10 +76,5 @@ Use `time fastfetch --stat` to show time usage for each module. ## If fastfetch behaves incorrectly on shell starting - - * The bug is reproduceable with fresh / clean shell configuration (i.e. `fastfetch` is the single line of `.zshrc` or `~/.config/fish/config.fish`): * Does `sleep 1` before running `fastfetch` work? diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 287db4ea3..01ff21b4f 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -484,7 +484,7 @@ jobs: with: msystem: CLANG32 update: true - install: git mingw-w64-clang-i686-7zip mingw-w64-clang-i686-cmake mingw-w64-clang-i686-clang mingw-w64-clang-i686-vulkan-loader mingw-w64-clang-i686-vulkan-headers mingw-w64-clang-i686-opencl-icd + install: git p7zip mingw-w64-clang-i686-cmake mingw-w64-clang-i686-clang mingw-w64-clang-i686-vulkan-loader mingw-w64-clang-i686-vulkan-headers mingw-w64-clang-i686-opencl-icd - name: print msys version run: uname -a diff --git a/CHANGELOG.md b/CHANGELOG.md index 1869906db..9daf30a80 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,3 +1,39 @@ +# 2.13.0 + +Changes: +* Option `--gpu-force-vulkan ` has been changed to `--gpu-detection-method ` + * Use `--gpu-detection-method vulkan` to get the old behavior + * See `fastfetch -h gpu-detection-method` for detail +* In Linux, BIOS limited CPU frequency is printed by default to match the behavior of neofetch (CPU, Linux, #947) + +Features: +* Add new module `Bootmgr` which prints information of stage 2 bootloader (grub, system-boot, etc) + * Requires root permission to work on Windows and FreeBSD + * Requires booting in UEFI mode +* Add package manager lpkg and lpkg-build support (Packages, Linux) +* Improve macOS 10.13 compatibility (macOS) +* Detect core count for performance / efficiency cores (CPU) + * Test it with `fastfetch -s cpu --cpu-format '{9}'` +* Support min / max frequency and physical core count detection in FreeBSD, if kernel supports it (CPU, FreeBSD) +* Detect DRM driver version if DRM detection method is used (GPU, Linux) + +Bugfixes: +* Don't detect `clifm` and `valgrind` as a terminal (Terminal, Linux) +* Improve stability (PhysicalMemory, FreeBSD) +* Fix bssid & status detection (Wifi, FreeBSD) +* Ensure createTime is correctly initialized (Disk, FreeBSD / macOS) +* Fix `--cpu-freq-ndigits` not working if `--cpu-format` is used +* Fix `nix-user` package count detection (Packages, Linux) +* Fix some memory leaks + +Logos: +* Fix Manjaro logo not displayed +* Add SpoinkOS +* Add Loc-OS +* Add Furreto Linux +* Fix TorizonCore logo colors +* Fix KDE neon logo not displayed + # 2.12.0 Changes: @@ -20,6 +56,7 @@ Features: * Add option `--localip-show-all-ips` to show all IPs assigned to the same interface (LocalIP) * Default to `false` * Improve compatibility with `(*term)` (#909, Terminal, macOS) +* Support GPU core count and frequency detection for Asahi Linux (GPU, Linux) Bugfixes: * Rename option `--temperature-unit` to `--temp-unit` as documented in help messages diff --git a/CMakeLists.txt b/CMakeLists.txt index 735998c1a..e3c528cc1 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -1,7 +1,7 @@ cmake_minimum_required(VERSION 3.12.0) # target_link_libraries with OBJECT libs & project homepage url project(fastfetch - VERSION 2.12.0 + VERSION 2.13.0 LANGUAGES C DESCRIPTION "Fast neofetch-like system information tool" HOMEPAGE_URL "https://github.com/fastfetch-cli/fastfetch" @@ -115,7 +115,7 @@ endif() if(WIN32) set(CMAKE_EXE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS} -Wl,--tsaware -Wl,--build-id -Wl,--subsystem,console:6.1,--major-os-version,6,--minor-os-version,1") -elseif(APPLE) +elseif(APPLE AND CMAKE_C_COMPILER_ID MATCHES "Clang") set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -fobjc-arc") endif() @@ -284,6 +284,7 @@ set(LIBFASTFETCH_SRC src/common/properties.c src/common/settings.c src/common/temps.c + src/detection/bootmgr/bootmgr.c src/detection/chassis/chassis.c src/detection/cpu/cpu.c src/detection/cpuusage/cpuusage.c @@ -314,6 +315,7 @@ set(LIBFASTFETCH_SRC src/modules/bios/bios.c src/modules/bluetooth/bluetooth.c src/modules/board/board.c + src/modules/bootmgr/bootmgr.c src/modules/brightness/brightness.c src/modules/break/break.c src/modules/camera/camera.c @@ -394,6 +396,7 @@ if(LINUX) src/detection/battery/battery_linux.c src/detection/bios/bios_linux.c src/detection/board/board_linux.c + src/detection/bootmgr/bootmgr_linux.c src/detection/brightness/brightness_linux.c src/detection/chassis/chassis_linux.c src/detection/cpu/cpu_linux.c @@ -462,6 +465,7 @@ elseif(ANDROID) src/detection/bios/bios_android.c src/detection/bluetooth/bluetooth_nosupport.c src/detection/board/board_android.c + src/detection/bootmgr/bootmgr_nosupport.c src/detection/brightness/brightness_nosupport.c src/detection/chassis/chassis_nosupport.c src/detection/cpu/cpu_linux.c @@ -520,6 +524,7 @@ elseif(BSD) src/detection/bios/bios_bsd.c src/detection/bluetooth/bluetooth_linux.c src/detection/board/board_bsd.c + src/detection/bootmgr/bootmgr_bsd.c src/detection/brightness/brightness_bsd.c src/detection/chassis/chassis_bsd.c src/detection/cpu/cpu_bsd.c @@ -588,6 +593,7 @@ elseif(APPLE) src/detection/bios/bios_apple.c src/detection/bluetooth/bluetooth_apple.m src/detection/board/board_apple.c + src/detection/bootmgr/bootmgr_apple.c src/detection/brightness/brightness_apple.c src/detection/chassis/chassis_nosupport.c src/detection/cpu/cpu_apple.c @@ -647,6 +653,7 @@ elseif(WIN32) src/detection/bios/bios_windows.c src/detection/bluetooth/bluetooth_windows.c src/detection/board/board_windows.c + src/detection/bootmgr/bootmgr_windows.c src/detection/brightness/brightness_windows.cpp src/detection/chassis/chassis_windows.c src/detection/cpu/cpu_windows.c diff --git a/README.md b/README.md index f2087551d..814084c89 100644 --- a/README.md +++ b/README.md @@ -101,11 +101,90 @@ Logos can be heavily customized too; see the [logo documentation](https://github ## FAQ -Q: Why do you need a very performant version of neofetch? -> I like putting neofetch in my ~/.bashrc to have a system overview whenever I use the terminal, but the slow speed annoyed me, so I created this. Also neofetch didn't output everything correctly (e.g Font is displayed as "[Plasma], Noto Sans, 10 [GTK2/3]") and writing my own tool gave me the possibility to fine tune it to run perfectly on at least my configuration. +### Q: Neofetch is good enough. Why do I need fastfetch? -Q: It does not display [*] correctly for me, what can I do? -> This is most likely because your system is not implemented (yet). At the moment I am focusing more on making the core app better, than adding more configurations. Feel free to open a pull request if you want to add support for your configuration +1. Fastfetch is actively maintained. +2. Fastfetch is faster. As the name suggests. +3. Fastfetch is more feature-rich. By default fastfetch only has a few modules enabled. Use `fastfetch -c all` to find what you want. +4. Fastfetch is more configurable. You can find more information in the Wiki: +5. Fastfetch is more polished. For example, neofetch prints `555MiB` in `Memory` module and `23G` in `Disk` module (notibily the difference of `MiB` and `G`), while fastfetch prints `555.00 MiB` and `22.97 GiB` respectively. +6. Fastfetch is more accurate. For example, [neofetch never actually supports Wayland protocol](https://github.com/dylanaraps/neofetch/pull/2395). + +### Q: Fastfetch shows my local IP address. It leaks my privacy! + +A local IP (10.x.x.x, 172.x.x.x, 192.168.x.x) has nothing to do with privacy. It only makes sense if you are on the same network, for example, if you connect to the same Wi-Fi network. + +Actually the `Local IP` module is the most useful module for me personally. I (@CarterLi) have several VMs installed to test fastfetch and often need to SSH into them. I have fastfetch running on shell startup and I never need to type `ip addr` manually. + +If you really don't like it, you can disable the `Local IP` module in `config.jsonc`. + +### Q: Where is the config file? I can't find it. + +`Fastfetch` don't generate config file automatically. You can use `fastfetch --gen-config` to generate one. The config file will be saved in `~/.config/fastfetch/config.jsonc` by default. See [Wiki for detail](https://github.com/fastfetch-cli/fastfetch/wiki/Configuration). + +### Q: The configuration is so complex. Where is the documentation? + +Fastfetch uses JSON (with comments) for configuration. I suggest you use an IDE with JSON schema support (like VSCode) to edit it. + +Alternatively, you can refer to the presets in [`presets` directory](https://github.com/fastfetch-cli/fastfetch/tree/dev/presets). + +### Q: I WANT THE DOCUMENTATION! + +[Here is the documentation](https://github.com/fastfetch-cli/fastfetch/wiki/Json-Schema). It is generated from [JSON schema](https://github.com/fastfetch-cli/fastfetch/blob/dev/doc/json_schema.json) but you won't like it. + +### Q: How can I customize the module output? + +Fastfetch uses `format` to generate output. For example to make `GPU` module show GPU name only and ignore other information, you can use + +```jsonc +{ + "modules": [ + { + "type": "gpu", + "format": "{2}" // See `fastfetch -h gpu-format` for detail + } + ] +} +``` + +which is equivalent to `fastfetch -s gpu --gpu-format '{2}'` + +See `fastfetch -h format` for basic usage. For module specific formattion, see `fastfetch -h -format` + +### Q: I have my own ascii-art / image file. How can I show it with fastfetch? + +Try `fastfetch -l /path/to/logo`. See [logo documentation](https://github.com/fastfetch-cli/fastfetch/wiki/Logo-options) for detail. + +### Q: Fastfetch runs in white and black on shell startup. Why? + +This issue usually happens when using fastfetch with `p10k`. There are known incompatibility between fastfetch and p10k instant prompt. +The p10k doc clearly states that you should NOT print anything to stdout after `p10k-instant-prompt` is initialized. You should either put `fastfetch` before initialization of `p10k-instant-prompt` (recommended) + +You can always use `fastfetch --pipe false` to force fastfetch running in colorful mode. + +### Q: I want feature A / B / C. Will fastfetch support it? + +Fastfetch is a system information tool. We only accept hardware or system level software feature requests. For most personal uses, I recommend using `Command` module to detect it yourself, which can be used to grab output from a custom shell script: + +```jsonc +// This module shows the default editor +{ + "modules": [ + { + "type": "command", + "text": "$EDITOR --version | head -1", + "key": "Editor" + } + ] +} +``` + +Otherwise, open a feature request in [GitHub Issues](https://github.com/fastfetch-cli/fastfetch/issues). + +### Q: I have questions. Where can I get help? + +* For usage questions, please start a discussion in [GitHub Discussions](https://github.com/fastfetch-cli/fastfetch/discussions). +* For possible bugs, please open an issue in [GitHub Issues](https://github.com/fastfetch-cli/fastfetch/issues). Be sure to fill the bug-report template carefully for developers to investigate. ## Star History diff --git a/completions/bash b/completions/bash index d3d6964c7..9ae14e00d 100644 --- a/completions/bash +++ b/completions/bash @@ -197,7 +197,7 @@ __fastfetch_completion() "--disk-show-subvolumes" "--gpu-hide-integrated" "--gpu-hide-discrete" - "--gpu-force-vulkan" + "--gpu-force-method" "--disk-show-unknown" "--bluetooth-show-disconnected" ) diff --git a/completions/fish b/completions/fish index a078a795a..a1713e729 100755 --- a/completions/fish +++ b/completions/fish @@ -85,8 +85,7 @@ string match -r -a -g "^###> ?(.*)" < (status -f) | string collect | python3 | s ###> elif type == 'config': ###> print(f'{command_prefix} -x -a "(__fastfetch_complete_config)"') ###> elif type == 'enum': -###> temp: str = ' '.join(map(lambda x: f"{x[0]}\\t{x[1]}", flag["arg"]["enum"].items())) -###> print(f'{command_prefix} -x -a "{temp}"') +###> print(f'{command_prefix} -x -a "{' '.join(flag["arg"]["enum"])}"') ###> elif type == 'logo': ###> print(f'{command_prefix} -x -a "(__fastfetch_complete_logo)"') ###> elif type == 'structure': diff --git a/doc/json_schema.json b/doc/json_schema.json index dc43ff932..51b3301d8 100644 --- a/doc/json_schema.json +++ b/doc/json_schema.json @@ -680,6 +680,7 @@ "bios", "bluetooth", "board", + "bootmgr", "break", "brightness", "camera", @@ -713,6 +714,7 @@ "os", "packages", "physicaldisk", + "physicalmemory", "player", "poweradapter", "processes", @@ -771,12 +773,16 @@ "anyOf": [ { "const": "bios", - "description": "Print BIOS name, version, release date, etc" + "description": "Print information of 1st-stage bootloader (name, version, release date, etc)" }, { "const": "board", "description": "Print mather board name and other info" }, + { + "const": "bootmgr", + "description": "Print information of 2nd-stage bootloader (name, firmware, etc)" + }, { "const": "camera", "description": "Print available cameras" @@ -829,6 +835,10 @@ "const": "os", "description": "Print operating system name and version" }, + { + "const": "physicalmemory", + "description": "Print system physical memory devices" + }, { "const": "player", "description": "Print music player name" @@ -1445,10 +1455,16 @@ "type": "boolean", "default": false }, - "forceVulkan": { - "description": "Force using vulkan to detect GPUs, which support video memory usage detection with `--allow-slow-operations`", - "type": "boolean", - "default": false + "detectionMethod": { + "description": "Force using a specified method to detect GPUs", + "type": "string", + "enum": [ + "auto", + "pci", + "vulkan", + "opengl" + ], + "default": "auto" }, "hideType": { "description": "Specify the type of GPUs should not be printed", diff --git a/src/common/modules.c b/src/common/modules.c index ea9958eb3..84a22658d 100644 --- a/src/common/modules.c +++ b/src/common/modules.c @@ -9,6 +9,7 @@ static FFModuleBaseInfo* B[] = { (void*) &instance.config.modules.bios, (void*) &instance.config.modules.bluetooth, (void*) &instance.config.modules.board, + (void*) &instance.config.modules.bootmgr, (void*) &instance.config.modules.break_, (void*) &instance.config.modules.brightness, NULL, diff --git a/src/data/help.json b/src/data/help.json index 7e3631e35..74dde8d45 100644 --- a/src/data/help.json +++ b/src/data/help.json @@ -1197,13 +1197,18 @@ } }, { - "long": "gpu-force-vulkan", - "desc": "Force using vulkan to detect GPUs", - "remark": "Vulkan supports video memory usage detection", + "long": "gpu-detection-method", + "desc": "Force using a specified method to detect GPUs", + "remark": "Methods are used in this order: DRM (Linux only) -> PCI (Linux, FreeBSD and other platform specific methods) -> Vulkan -> OpenGL", "arg": { - "type": "bool", - "optional": true, - "default": false + "type": "enum", + "enum": { + "auto": "Use DRM if available, which is most feature-rich. Requires proper DRM driver installed", + "pci": "Search PCI devices, which do not requires any driver installed", + "vulkan": "Use Vulkan API. Slow and requires vulkan driver installed", + "opengl": "Use OpenGL API. Slow and only detects one GPU" + }, + "default": "auto" } }, { diff --git a/src/detection/battery/battery_windows.c b/src/detection/battery/battery_windows.c index 5a0a78a3c..1d507c8c6 100644 --- a/src/detection/battery/battery_windows.c +++ b/src/detection/battery/battery_windows.c @@ -1,6 +1,7 @@ #include "battery.h" #include "common/io/io.h" +#include "util/windows/nt.h" #include "util/windows/unicode.h" #include "util/mallocHelper.h" #include "util/smbiosHelper.h" @@ -12,16 +13,6 @@ #include #include -NTSYSCALLAPI -NTSTATUS -NTAPI -NtPowerInformation( - IN POWER_INFORMATION_LEVEL InformationLevel, - IN PVOID InputBuffer OPTIONAL, - IN ULONG InputBufferLength, - OUT PVOID OutputBuffer OPTIONAL, - IN ULONG OutputBufferLength); - static inline void wrapSetupDiDestroyDeviceInfoList(HDEVINFO* hdev) { if(*hdev) diff --git a/src/detection/bios/bios_linux.c b/src/detection/bios/bios_linux.c index fa794d1c2..86342c159 100644 --- a/src/detection/bios/bios_linux.c +++ b/src/detection/bios/bios_linux.c @@ -4,7 +4,7 @@ #include -const char* ffDetectBios(FFBiosResult* bios) +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); diff --git a/src/detection/bios/bios_windows.c b/src/detection/bios/bios_windows.c index d8a305493..d7a954074 100644 --- a/src/detection/bios/bios_windows.c +++ b/src/detection/bios/bios_windows.c @@ -1,5 +1,6 @@ #include "bios.h" #include "util/smbiosHelper.h" +#include "util/windows/registry.h" #include #include diff --git a/src/detection/bootmgr/bootmgr.c b/src/detection/bootmgr/bootmgr.c new file mode 100644 index 000000000..9d4dfa838 --- /dev/null +++ b/src/detection/bootmgr/bootmgr.c @@ -0,0 +1,50 @@ +#include "efi_helper.h" + +static inline uint8_t evBits(uint16_t val, uint8_t mask, uint8_t shift) +{ + return (uint8_t) ((val & (mask << shift)) >> shift); +} + +static void ffEfiUcs2ToUtf8(const uint16_t *const chars, FFstrbuf* result) +{ + for (uint32_t i = 0; chars[i]; i++) + { + if (chars[i] <= 0x007f) + ffStrbufAppendC(result, (char) chars[i]); + else if (chars[i] > 0x007f && chars[i] <= 0x07ff) + { + ffStrbufAppendC(result, (char) (0xc0 | evBits(chars[i], 0x1f, 6))); + ffStrbufAppendC(result, (char) (0x80 | evBits(chars[i], 0x3f, 0))); + } + else + { + ffStrbufAppendC(result, (char) (0xe0 | evBits(chars[i], 0xf, 12))); + ffStrbufAppendC(result, (char) (0x80 | evBits(chars[i], 0x3f, 6))); + ffStrbufAppendC(result, (char) (0x80 | evBits(chars[i], 0x3f, 0))); + } + } +} + +bool ffEfiFillLoadOption(const FFEfiLoadOption* efiOption, FFBootmgrResult* result) +{ + uint32_t descLen = 0; + while (efiOption->Description[descLen]) ++descLen; + + if (descLen) + ffEfiUcs2ToUtf8(efiOption->Description, &result->name); + + for ( + ffEfiDevicePathProtocol* filePathList = (void*) &efiOption->Description[descLen + 1]; + filePathList->Type != 0x7F; // End of Hardware Device Path + filePathList = (void*) ((uint8_t*) filePathList + filePathList->Length)) + { + if (filePathList->Type == 4 && filePathList->SubType == 4) + { + // https://uefi.org/specs/UEFI/2.10/10_Protocols_Device_Path_Protocol.html#file-path-media-device-path + ffEfiUcs2ToUtf8((uint16_t*) filePathList->SpecificDevicePathData, &result->firmware); + return true; + } + } + + return false; +} diff --git a/src/detection/bootmgr/bootmgr.h b/src/detection/bootmgr/bootmgr.h new file mode 100644 index 000000000..082149e8e --- /dev/null +++ b/src/detection/bootmgr/bootmgr.h @@ -0,0 +1,12 @@ +#pragma once + +#include "fastfetch.h" + +typedef struct FFBootmgrResult +{ + FFstrbuf name; + FFstrbuf firmware; + bool secureBoot; +} FFBootmgrResult; + +const char* ffDetectBootmgr(FFBootmgrResult* bios); diff --git a/src/detection/bootmgr/bootmgr_apple.c b/src/detection/bootmgr/bootmgr_apple.c new file mode 100644 index 000000000..bd4c5dc98 --- /dev/null +++ b/src/detection/bootmgr/bootmgr_apple.c @@ -0,0 +1,12 @@ +#include "bootmgr.h" +#include "common/io/io.h" + +const char* ffDetectBootmgr(FFBootmgrResult* result) +{ + if (ffPathExists("/System/Library/CoreServices/boot.efi", FF_PATHTYPE_FILE)) + ffStrbufSetStatic(&result->firmware, "/System/Library/CoreServices/boot.efi"); + + ffStrbufSetStatic(&result->name, "iBoot"); + + return NULL; +} diff --git a/src/detection/bootmgr/bootmgr_bsd.c b/src/detection/bootmgr/bootmgr_bsd.c new file mode 100644 index 000000000..86fe86fac --- /dev/null +++ b/src/detection/bootmgr/bootmgr_bsd.c @@ -0,0 +1,43 @@ +#include "bootmgr.h" +#include "efi_helper.h" +#include "common/io/io.h" + +#include +#include +#include + +const char* ffDetectBootmgr(FFBootmgrResult* result) +{ + FF_AUTO_CLOSE_FD int efifd = open("/dev/efi", O_RDWR); + if (efifd < 0) return "open(/dev/efi) failed"; + + uint8_t buffer[2048]; + struct efi_var_ioc ioc = { + .vendor = { 0x8be4df61, 0x93ca, 0x11d2, 0xaa, 0x0d, { 0x00, 0xe0, 0x98, 0x03, 0x2b, 0x8c } }, + .data = buffer, + }; + + ioc.datasize = sizeof(buffer); + ioc.name = (efi_char[]){ 'B', 'o', 'o', 't', 'C', 'u', 'r', 'r', 'e', 'n', 't', '\0' }; + ioc.namesize = sizeof("BootCurrent") * 2; + if (ioctl(efifd, EFIIOC_VAR_GET, &ioc) < 0 || ioc.datasize != 2) + return "ioctl(EFIIOC_VAR_GET, BootCurrent) failed"; + + unsigned char hex[5]; + snprintf((char*) hex, sizeof(hex), "%04X", *(uint16_t*)buffer); + ioc.datasize = sizeof(buffer); + ioc.name = (efi_char[]){ 'B', 'o', 'o', 't', hex[0], hex[1], hex[2], hex[3], '\0' }; + ioc.namesize = sizeof("Boot####") * 2; + if (ioctl(efifd, EFIIOC_VAR_GET, &ioc) < 0 || ioc.datasize == sizeof(buffer)) + return "ioctl(EFIIOC_VAR_GET, Boot####) failed"; + + ffEfiFillLoadOption((FFEfiLoadOption *)buffer, result); + + ioc.name = (efi_char[]){ 'S', 'e', 'c', 'u', 'r', 'e', 'B', 'o', 'o', 't', '\0' }; + ioc.namesize = sizeof("SecureBoot") * 2; + ioc.datasize = sizeof(buffer); + if (ioctl(efifd, EFIIOC_VAR_GET, &ioc) == 0 && ioc.datasize == 1) + result->secureBoot = !!buffer[0]; + + return NULL; +} diff --git a/src/detection/bootmgr/bootmgr_linux.c b/src/detection/bootmgr/bootmgr_linux.c new file mode 100644 index 000000000..e1e11f967 --- /dev/null +++ b/src/detection/bootmgr/bootmgr_linux.c @@ -0,0 +1,27 @@ +#include "bootmgr.h" +#include "common/io/io.h" +#include "efi_helper.h" + +#define FF_EFIVARS_PATH_PREFIX "/sys/firmware/efi/efivars/" + +const char* ffDetectBootmgr(FFBootmgrResult* result) +{ + 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"; + + uint16_t value = *(uint16_t *)&buffer[4]; + snprintf((char*) buffer, sizeof(buffer), FF_EFIVARS_PATH_PREFIX "Boot%04X-" FF_EFI_GLOBAL_GUID, value); + + ssize_t size = ffReadFileData((const char*) buffer, sizeof(buffer), buffer); + if (size < 5 + (int) sizeof(FFEfiLoadOption) || size == (ssize_t) sizeof(buffer)) + return "Failed to read efivar: Boot####"; + + ffEfiFillLoadOption((FFEfiLoadOption *)&buffer[4], result); + + if (ffReadFileData(FF_EFIVARS_PATH_PREFIX "SecureBoot-" FF_EFI_GLOBAL_GUID, sizeof(buffer), buffer) == 6) + result->secureBoot = buffer[4] == 1; + + return NULL; +} diff --git a/src/detection/bootmgr/bootmgr_nosupport.c b/src/detection/bootmgr/bootmgr_nosupport.c new file mode 100644 index 000000000..a61cce63b --- /dev/null +++ b/src/detection/bootmgr/bootmgr_nosupport.c @@ -0,0 +1,6 @@ +#include "bootmgr.h" + +const char* ffDetectBootmgr(FF_MAYBE_UNUSED FFBootmgrResult* result) +{ + return "Not supported on this platform"; +} diff --git a/src/detection/bootmgr/bootmgr_windows.c b/src/detection/bootmgr/bootmgr_windows.c new file mode 100644 index 000000000..e3c687d02 --- /dev/null +++ b/src/detection/bootmgr/bootmgr_windows.c @@ -0,0 +1,53 @@ +#include "bootmgr.h" +#include "efi_helper.h" + +#include + +const char* enablePrivilege(const wchar_t* privilege) +{ + HANDLE token; + if (!OpenProcessToken(GetCurrentProcess(), TOKEN_ADJUST_PRIVILEGES | TOKEN_QUERY, &token)) + return "OpenProcessToken() failed"; + + TOKEN_PRIVILEGES tp = { + .PrivilegeCount = 1, + .Privileges = { + (LUID_AND_ATTRIBUTES) { .Attributes = SE_PRIVILEGE_ENABLED } + }, + }; + if (!LookupPrivilegeValueW(NULL, privilege, &tp.Privileges[0].Luid)) + return "LookupPrivilegeValue() failed"; + + if (!AdjustTokenPrivileges(token, false, &tp, sizeof(tp), NULL, NULL)) + return "AdjustTokenPrivileges() failed"; + + if (GetLastError() == ERROR_NOT_ALL_ASSIGNED) + return "The token does not have the specified privilege"; + + return NULL; +} + +const char* ffDetectBootmgr(FFBootmgrResult* result) +{ + if (enablePrivilege(L"SeSystemEnvironmentPrivilege") != NULL) + return "Failed to enable SeSystemEnvironmentPrivilege"; + + uint16_t value; + if (GetFirmwareEnvironmentVariableW(L"BootCurrent", L"{" FF_EFI_GLOBAL_GUID L"}", &value, sizeof(value)) != 2) + return "GetFirmwareEnvironmentVariableW(BootCurrent) failed"; + + uint8_t buffer[2048]; + wchar_t key[16]; + wsprintfW(key, L"Boot%04X", value); + uint32_t size = GetFirmwareEnvironmentVariableW(key, L"{" FF_EFI_GLOBAL_GUID L"}", buffer, sizeof(buffer)); + if (size < sizeof(FFEfiLoadOption) || size == sizeof(buffer)) + return "GetFirmwareEnvironmentVariableW(Boot####) failed"; + + ffEfiFillLoadOption((FFEfiLoadOption *)buffer, result); + + DWORD uefiSecureBootEnabled = 0, bufSize = 0; + if (RegGetValueW(HKEY_LOCAL_MACHINE, L"SYSTEM\\ControlSet001\\Control\\SecureBoot\\State", L"UEFISecureBootEnabled", RRF_RT_REG_DWORD, NULL, &uefiSecureBootEnabled, &bufSize) == ERROR_SUCCESS) + result->secureBoot = !!uefiSecureBootEnabled; + + return NULL; +} diff --git a/src/detection/bootmgr/efi_helper.h b/src/detection/bootmgr/efi_helper.h new file mode 100644 index 000000000..d650c8e97 --- /dev/null +++ b/src/detection/bootmgr/efi_helper.h @@ -0,0 +1,24 @@ +#include "bootmgr.h" + +// https://uefi.org/specs/UEFI/2.10/10_Protocols_Device_Path_Protocol.html#generic-device-path-structures +typedef struct ffEfiDevicePathProtocol +{ + uint8_t Type; + uint8_t SubType; + uint16_t Length; + uint8_t SpecificDevicePathData[]; +} ffEfiDevicePathProtocol; + +// https://uefi.org/specs/UEFI/2.10/03_Boot_Manager.html#load-options +typedef struct FFEfiLoadOption +{ + uint32_t Attributes; + uint16_t FilePathListLength; + uint16_t Description[]; + // ffEfiDevicePathProtocol FilePathList[]; + // uint8_t OptionalData[]; +} FFEfiLoadOption; + +bool ffEfiFillLoadOption(const FFEfiLoadOption* efiOption, FFBootmgrResult* result); + +#define FF_EFI_GLOBAL_GUID "8be4df61-93ca-11d2-aa0d-00e098032b8c" diff --git a/src/detection/camera/camera_apple.m b/src/detection/camera/camera_apple.m index 66e18a7d6..ab15fb99e 100644 --- a/src/detection/camera/camera_apple.m +++ b/src/detection/camera/camera_apple.m @@ -8,6 +8,7 @@ const char* ffDetectCamera(FFlist* result) { + #ifdef MAC_OS_X_VERSION_10_15 FF_SUPPRESS_IO(); // #822 AVCaptureDeviceType deviceType; @@ -47,6 +48,8 @@ camera->width = size.width < 0 ? 0 : (uint32_t) size.width; camera->height = size.height < 0 ? 0 : (uint32_t) size.height; } - return NULL; + #else + return "No support for old MacOS version"; + #endif } diff --git a/src/detection/cpu/cpu.h b/src/detection/cpu/cpu.h index bba2aba47..fc9b7e080 100644 --- a/src/detection/cpu/cpu.h +++ b/src/detection/cpu/cpu.h @@ -4,6 +4,12 @@ #define FF_CPU_TEMP_UNSET (0/0.0) +typedef struct FFCPUCore +{ + uint32_t freq; + uint32_t count; +} FFCPUCore; + typedef struct FFCPUResult { FFstrbuf name; @@ -16,10 +22,12 @@ typedef struct FFCPUResult double frequencyBase; // GHz double frequencyMax; // GHz double frequencyMin; // GHz + double frequencyBiosLimit; // GHz + + FFCPUCore coreTypes[16]; // number of P cores, E cores, etc. double temperature; } FFCPUResult; -const char* ffCPUDetectByCpuid(FFCPUResult* cpu); const char* ffDetectCPU(const FFCPUOptions* options, FFCPUResult* cpu); const char* ffCPUAppleCodeToName(uint32_t code); diff --git a/src/detection/cpu/cpu_apple.c b/src/detection/cpu/cpu_apple.c index de3b18707..efffa6d41 100644 --- a/src/detection/cpu/cpu_apple.c +++ b/src/detection/cpu/cpu_apple.c @@ -68,20 +68,37 @@ static const char* detectFrequency(FFCPUResult* cpu) { cpu->frequencyBase = ffSysctlGetInt64("hw.cpufrequency", 0) / 1000.0 / 1000.0 / 1000.0; cpu->frequencyMin = ffSysctlGetInt64("hw.cpufrequency_min", 0) / 1000.0 / 1000.0 / 1000.0; - cpu->frequencyMax = ffSysctlGetInt64("hw.cpufrequency_max", 0); - if(cpu->frequencyMax > 0.0) - cpu->frequencyMax /= 1000.0 * 1000.0 * 1000.0; - else + cpu->frequencyMax = ffSysctlGetInt64("hw.cpufrequency_max", 0) / 1000.0 / 1000.0 / 1000.0; + if(cpu->frequencyBase != cpu->frequencyBase) { unsigned current = 0; size_t size = sizeof(current); if (sysctl((int[]){ CTL_HW, HW_CPU_FREQ }, 2, ¤t, &size, NULL, 0) == 0) - cpu->frequencyMax = (double) current / 1000.0 / 1000.0 / 1000.0; + cpu->frequencyBase = (double) current / 1000.0 / 1000.0 / 1000.0; } return NULL; } #endif +static const char* detectCoreCount(FFCPUResult* cpu) +{ + uint32_t nPerfLevels = (uint32_t) ffSysctlGetInt("hw.nperflevels", 0); + if (nPerfLevels <= 0) return "sysctl(hw.nperflevels) failed"; + + char sysctlKey[] = "hw.perflevelN.logicalcpu"; + if (nPerfLevels > sizeof(cpu->coreTypes) / sizeof(cpu->coreTypes[0])) + nPerfLevels = sizeof(cpu->coreTypes) / sizeof(cpu->coreTypes[0]); + for (uint32_t i = 0; i < nPerfLevels; ++i) + { + sysctlKey[strlen("hw.perflevel")] = (char) ('0' + i); + cpu->coreTypes[i] = (FFCPUCore) { + .freq = nPerfLevels - i, + .count = (uint32_t) ffSysctlGetInt(sysctlKey, 0), + }; + } + return NULL; +} + const char* ffDetectCPUImpl(const FFCPUOptions* options, FFCPUResult* cpu) { if (ffSysctlGetString("machdep.cpu.brand_string", &cpu->name) != NULL) @@ -104,6 +121,7 @@ const char* ffDetectCPUImpl(const FFCPUOptions* options, FFCPUResult* cpu) cpu->coresOnline = (uint16_t) ffSysctlGetInt("hw.activecpu", 1); detectFrequency(cpu); + detectCoreCount(cpu); cpu->temperature = options->temp ? detectCpuTemp(&cpu->name) : FF_CPU_TEMP_UNSET; diff --git a/src/detection/cpu/cpu_bsd.c b/src/detection/cpu/cpu_bsd.c index 70623cbff..80f767ca8 100644 --- a/src/detection/cpu/cpu_bsd.c +++ b/src/detection/cpu/cpu_bsd.c @@ -11,6 +11,62 @@ const char* ffDetectCPUImpl(const FFCPUOptions* options, FFCPUResult* cpu) cpu->coresLogical = cpu->coresPhysical; cpu->coresOnline = cpu->coresPhysical; + FF_STRBUF_AUTO_DESTROY buffer = ffStrbufCreate(); + if (ffSysctlGetString("kern.sched.topology_spec", &buffer) == NULL && buffer.length > 0) + { + // + // + // 0, 1, 2, 3 + // + // + // 0, 1 + // THREAD groupSMT group + // + // + // 2, 3 + // THREAD groupSMT group + // + // + // + // + uint32_t i = 0; + while (true) + { + i = ffStrbufNextIndexS(&buffer, i, "THREAD group"); // Find physical core with hyper-threading enabled + if (i >= buffer.length) break; + cpu->coresPhysical--; + i += (uint32_t) strlen("THREAD group"); + } + } + + uint32_t ifreq = (uint32_t) -1; + for (uint16_t i = 0; i < cpu->coresLogical; ++i) + { + ffStrbufClear(&buffer); + char key[32]; + snprintf(key, sizeof(key), "dev.cpu.%u.freq_levels", i); + if (ffSysctlGetString(key, &buffer) == NULL && buffer.length > 0) + { + // MHz/Watts pairs like: 2501/32000 2187/27125 2000/24000 + uint32_t fmax = (uint32_t) strtoul(buffer.chars, NULL, 10); + uint32_t fmin = fmax; + uint32_t i = ffStrbufLastIndexC(&buffer, ' '); + if (i < buffer.length) + fmin = (uint32_t) strtoul(buffer.chars + i + 1, NULL, 10); + if (!(cpu->frequencyMin <= fmin)) cpu->frequencyMin = fmin; // Counting for NaN + if (!(cpu->frequencyMax >= fmax)) cpu->frequencyMax = fmax; + + uint32_t ifreq = 0; + while (cpu->coreTypes[ifreq].freq != fmax && cpu->coreTypes[ifreq].freq > 0) + ++ifreq; + if (cpu->coreTypes[ifreq].freq == 0) + cpu->coreTypes[ifreq].freq = fmax; + cpu->coreTypes[ifreq].count++; + } + } + cpu->frequencyMin /= 1000; + cpu->frequencyMax /= 1000; + int clockRate = ffSysctlGetInt("hw.clockrate", 0); cpu->frequencyBase = clockRate <= 0 ? 0.0/0.0 : clockRate / 1000.0; cpu->temperature = FF_CPU_TEMP_UNSET; diff --git a/src/detection/cpu/cpu_linux.c b/src/detection/cpu/cpu_linux.c index e100eb60c..c4c040bb2 100644 --- a/src/detection/cpu/cpu_linux.c +++ b/src/detection/cpu/cpu_linux.c @@ -88,14 +88,14 @@ static const char* parseCpuInfo(FFCPUResult* cpu, FFstrbuf* physicalCoresBuffer, return NULL; } -static double getFrequency(FFstrbuf* basePath, const char* cpuinfoFileName, const char* scalingFileName, FFstrbuf* buffer) +static uint32_t getFrequency(FFstrbuf* basePath, const char* cpuinfoFileName, const char* scalingFileName, FFstrbuf* buffer) { uint32_t baseLen = basePath->length; ffStrbufAppendS(basePath, cpuinfoFileName); bool ok = ffReadFileBuffer(basePath->chars, buffer); ffStrbufSubstrBefore(basePath, baseLen); if (ok) - return ffStrbufToDouble(buffer) / 1e6; + return (uint32_t) ffStrbufToUInt(buffer, 0); if (scalingFileName) { @@ -103,10 +103,28 @@ static double getFrequency(FFstrbuf* basePath, const char* cpuinfoFileName, cons ok = ffReadFileBuffer(basePath->chars, buffer); ffStrbufSubstrBefore(basePath, baseLen); if (ok) - return ffStrbufToDouble(buffer) / 1e6; + return (uint32_t) ffStrbufToUInt(buffer, 0); } - return 0.0/0.0; + return 0; +} + +static uint8_t getNumCores(FFstrbuf* basePath, FFstrbuf* buffer) +{ + uint32_t baseLen = basePath->length; + ffStrbufAppendS(basePath, "/affected_cpus"); + bool ok = ffReadFileBuffer(basePath->chars, buffer); + ffStrbufSubstrBefore(basePath, baseLen); + if (ok) + return (uint8_t) ffStrbufCountC(buffer, ' ') + 1; + + ffStrbufAppendS(basePath, "/related_cpus"); + ok = ffReadFileBuffer(basePath->chars, buffer); + ffStrbufSubstrBefore(basePath, baseLen); + if (ok) + return (uint8_t) ffStrbufCountC(buffer, ' ') + 1; + + return 0; } static bool detectFrequency(FFCPUResult* cpu) @@ -119,38 +137,58 @@ static bool detectFrequency(FFCPUResult* cpu) uint32_t baseLen = path.length; struct dirent* entry; - while((entry = readdir(dir)) != NULL) + while ((entry = readdir(dir)) != NULL) { if (ffStrStartsWith(entry->d_name, "policy") && isdigit(entry->d_name[strlen("policy")])) { ffStrbufAppendS(&path, entry->d_name); - double fbase = getFrequency(&path, "/base_frequency", NULL, &buffer); - if (fbase == fbase) + uint32_t fbase = getFrequency(&path, "/base_frequency", NULL, &buffer); + if (fbase > 0) { if (cpu->frequencyBase == cpu->frequencyBase) cpu->frequencyBase = cpu->frequencyBase > fbase ? cpu->frequencyBase : fbase; else cpu->frequencyBase = fbase; } - double fmax = getFrequency(&path, "/cpuinfo_max_freq", "/scaling_max_freq", &buffer); - if (fmax == fmax) + uint32_t fbioslimit = getFrequency(&path, "/bios_limit", NULL, &buffer); + if (fbioslimit > 0) + { + if (cpu->frequencyBiosLimit == cpu->frequencyBiosLimit) + cpu->frequencyBiosLimit = cpu->frequencyBiosLimit > fbioslimit ? cpu->frequencyBiosLimit : fbioslimit; + else + cpu->frequencyBiosLimit = fbioslimit; + } + uint32_t fmax = getFrequency(&path, "/cpuinfo_max_freq", "/scaling_max_freq", &buffer); + if (fmax > 0) { if (cpu->frequencyMax == cpu->frequencyMax) cpu->frequencyMax = cpu->frequencyMax > fmax ? cpu->frequencyMax : fmax; else cpu->frequencyMax = fmax; } - double fmin = getFrequency(&path, "/cpuinfo_min_freq", "/scaling_min_freq", &buffer); - if (fmin == fmin) + uint32_t fmin = getFrequency(&path, "/cpuinfo_min_freq", "/scaling_min_freq", &buffer); + if (fmin > 0) { if (cpu->frequencyMin == cpu->frequencyMin) cpu->frequencyMin = cpu->frequencyMin < fmin ? cpu->frequencyMin : fmin; else cpu->frequencyMin = fmin; } + + uint32_t freq = fbase == 0 ? fmax : fbase; // seems base frequencies are more stable + uint32_t ifreq = 0; + while (cpu->coreTypes[ifreq].freq != freq && cpu->coreTypes[ifreq].freq > 0) + ++ifreq; + if (cpu->coreTypes[ifreq].freq == 0) + cpu->coreTypes[ifreq].freq = freq; + cpu->coreTypes[ifreq].count += getNumCores(&path, &buffer); ffStrbufSubstrBefore(&path, baseLen); } } + cpu->frequencyBase /= 1e6; + cpu->frequencyMax /= 1e6; + cpu->frequencyMin /= 1e6; + cpu->frequencyBiosLimit /= 1e6; return true; } diff --git a/src/detection/cpu/cpu_windows.c b/src/detection/cpu/cpu_windows.c index 0f3bb55d5..9e27c7eba 100644 --- a/src/detection/cpu/cpu_windows.c +++ b/src/detection/cpu/cpu_windows.c @@ -1,6 +1,7 @@ #include "cpu.h" #include "detection/temps/temps_windows.h" #include "util/windows/registry.h" +#include "util/windows/nt.h" #include "util/mallocHelper.h" #include "util/smbiosHelper.h" @@ -146,13 +147,6 @@ static const char* detectByRegistry(FFCPUResult* cpu) 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"; - if (detectSpeedByCpuid(cpu) != NULL || cpu->frequencyBase != cpu->frequencyBase) - { - uint32_t mhz; - if(ffRegReadUint(hKey, L"~MHz", &mhz, NULL)) - cpu->frequencyBase = mhz / 1000.0; - } - ffRegReadStrbuf(hKey, L"ProcessorNameString", &cpu->name, NULL); ffRegReadStrbuf(hKey, L"VendorIdentifier", &cpu->vendor, NULL); @@ -166,6 +160,27 @@ static const char* detectByRegistry(FFCPUResult* cpu) return NULL; } +static const char* detectCoreTypes(FFCPUResult* cpu) +{ + FF_AUTO_FREE PROCESSOR_POWER_INFORMATION* pinfo = calloc(cpu->coresLogical, sizeof(PROCESSOR_POWER_INFORMATION)); + if (!NT_SUCCESS(NtPowerInformation(ProcessorInformation, NULL, 0, pinfo, (ULONG) sizeof(PROCESSOR_POWER_INFORMATION) * cpu->coresLogical))) + return "NtPowerInformation(ProcessorInformation, NULL, 0, pinfo, size) failed"; + + for (uint32_t icore = 0; icore < cpu->coresLogical && pinfo[icore].MhzLimit; ++icore) + { + uint32_t ifreq = 0; + while (cpu->coreTypes[ifreq].freq != pinfo[icore].MhzLimit && cpu->coreTypes[ifreq].freq > 0) + ++ifreq; + if (cpu->coreTypes[ifreq].freq == 0) + cpu->coreTypes[ifreq].freq = pinfo[icore].MhzLimit; + ++cpu->coreTypes[ifreq].count; + } + + if (cpu->frequencyBase != cpu->frequencyBase) + cpu->frequencyBase = pinfo->MaxMhz / 1000.0; + return NULL; +} + const char* ffDetectCPUImpl(const FFCPUOptions* options, FFCPUResult* cpu) { detectNCores(cpu); @@ -174,6 +189,9 @@ const char* ffDetectCPUImpl(const FFCPUOptions* options, FFCPUResult* cpu) if (error) return error; + detectSpeedByCpuid(cpu); + detectCoreTypes(cpu); + if (cpu->frequencyMax != cpu->frequencyMax) detectMaxSpeedBySmbios(cpu); diff --git a/src/detection/disk/disk_bsd.c b/src/detection/disk/disk_bsd.c index dcd6cc507..3046a3577 100644 --- a/src/detection/disk/disk_bsd.c +++ b/src/detection/disk/disk_bsd.c @@ -66,6 +66,10 @@ static void detectFsInfo(struct statfs* fs, FFDisk* disk) #include #include +#ifndef MAC_OS_X_VERSION_10_15 + #define MNT_REMOVABLE 0x00000200 +#endif + struct CmnAttrBuf { uint32_t length; attrreference_t nameRef; @@ -132,13 +136,15 @@ const char* ffDetectDisksImpl(FFDiskOptions* options, FFlist* disks) ffStrbufInitS(&disk->filesystem, fs->f_fstypename); ffStrbufInit(&disk->name); disk->type = 0; + disk->createTime = 0; + detectFsInfo(fs, disk); if(fs->f_flags & MNT_RDONLY) disk->type |= FF_DISK_VOLUME_TYPE_READONLY_BIT; struct stat st; - if(stat(fs->f_mntonname, &st) == 0) + if(stat(fs->f_mntonname, &st) == 0 && st.st_birthtimespec.tv_sec > 0) disk->createTime = (uint64_t)((st.st_birthtimespec.tv_sec * 1000) + (st.st_birthtimespec.tv_nsec / 1000000)); } diff --git a/src/detection/displayserver/displayserver_apple.c b/src/detection/displayserver/displayserver_apple.c index 3a63fb303..964a6981f 100644 --- a/src/detection/displayserver/displayserver_apple.c +++ b/src/detection/displayserver/displayserver_apple.c @@ -8,6 +8,10 @@ #include extern CFDictionaryRef CoreDisplay_DisplayCreateInfoDictionary(CGDirectDisplayID display) __attribute__((weak_import)); +#ifndef MAC_OS_X_VERSION_10_15 +#import +extern CFDictionaryRef CoreDisplay_IODisplayCreateInfoDictionary(io_service_t framebuffer, IOOptionBits options) __attribute__((weak_import)); +#endif static void detectDisplays(FFDisplayServerResult* ds) { @@ -38,6 +42,7 @@ static void detectDisplays(FFDisplayServerResult* ds) } FF_STRBUF_AUTO_DESTROY name = ffStrbufCreate(); + #ifdef MAC_OS_X_VERSION_10_15 if(CoreDisplay_DisplayCreateInfoDictionary) { CFDictionaryRef FF_CFTYPE_AUTO_RELEASE displayInfo = CoreDisplay_DisplayCreateInfoDictionary(screen); @@ -48,6 +53,19 @@ static void detectDisplays(FFDisplayServerResult* ds) ffCfDictGetString(productNames, CFSTR("en_US"), &name); } } + #else + if(CoreDisplay_IODisplayCreateInfoDictionary) + { + io_service_t servicePort = CGDisplayIOServicePort(screen); + CFDictionaryRef FF_CFTYPE_AUTO_RELEASE displayInfo = CoreDisplay_IODisplayCreateInfoDictionary(servicePort, kIODisplayOnlyPreferredName); + if(displayInfo) + { + CFDictionaryRef productNames; + if(!ffCfDictGetDict(displayInfo, CFSTR(kDisplayProductName), &productNames)) + ffCfDictGetString(productNames, CFSTR("en_US"), &name); + } + } + #endif ffdsAppendDisplay(ds, (uint32_t)CGDisplayModeGetPixelWidth(mode), diff --git a/src/detection/font/font_apple.m b/src/detection/font/font_apple.m index 9a9888e5f..683acad03 100644 --- a/src/detection/font/font_apple.m +++ b/src/detection/font/font_apple.m @@ -25,7 +25,11 @@ static void generateString(FFFontResult* font) { ffStrbufAppendS(&result->fonts[0], [NSFont systemFontOfSize:12].familyName.UTF8String); ffStrbufAppendS(&result->fonts[1], [NSFont userFontOfSize:12].familyName.UTF8String); + #ifdef MAC_OS_X_VERSION_10_15 ffStrbufAppendS(&result->fonts[2], [NSFont monospacedSystemFontOfSize:12 weight:400].familyName.UTF8String); + #else + ffStrbufAppendS(&result->fonts[2], ""); + #endif ffStrbufAppendS(&result->fonts[3], [NSFont userFixedPitchFontOfSize:12].familyName.UTF8String); generateString(result); diff --git a/src/detection/gpu/gpu.c b/src/detection/gpu/gpu.c index 4f70d3452..b75ac7679 100644 --- a/src/detection/gpu/gpu.c +++ b/src/detection/gpu/gpu.c @@ -46,9 +46,9 @@ const char* detectByOpenGL(FFlist* gpus) { FFGPUResult* gpu = (FFGPUResult*) ffListAdd(gpus); gpu->type = FF_GPU_TYPE_UNKNOWN; - ffStrbufInitMove(&gpu->vendor, &result.vendor); + ffStrbufInit(&gpu->vendor); ffStrbufInitMove(&gpu->name, &result.renderer); - ffStrbufInit(&gpu->driver); + ffStrbufInitMove(&gpu->driver, &result.vendor); ffStrbufInitF(&gpu->platformApi, "OpenGL %s", result.version.chars); gpu->temperature = FF_GPU_TEMP_UNSET; gpu->coreCount = FF_GPU_CORE_COUNT_UNSET; @@ -56,22 +56,18 @@ const char* detectByOpenGL(FFlist* gpus) gpu->dedicated = gpu->shared = (FFGPUMemory){0, 0}; gpu->deviceId = 0; - if (ffStrbufIgnCaseEqualS(&gpu->vendor, "Mesa")) - ffStrbufClear(&gpu->vendor); - - if (!gpu->vendor.length) + if (ffStrbufContainS(&gpu->name, "Apple")) { - if (ffStrbufContainS(&gpu->name, "Apple")) - ffStrbufSetStatic(&gpu->vendor, FF_GPU_VENDOR_NAME_APPLE); - else if (ffStrbufContainS(&gpu->name, "Intel")) - ffStrbufSetStatic(&gpu->vendor, FF_GPU_VENDOR_NAME_INTEL); - else if (ffStrbufContainS(&gpu->name, "AMD") || ffStrbufContainS(&gpu->name, "ATI")) - ffStrbufSetStatic(&gpu->vendor, FF_GPU_VENDOR_NAME_AMD); - else if (ffStrbufContainS(&gpu->name, "NVIDIA")) - ffStrbufSetStatic(&gpu->vendor, FF_GPU_VENDOR_NAME_NVIDIA); - } - if (ffStrbufEqualS(&gpu->vendor, FF_GPU_VENDOR_NAME_APPLE)) + ffStrbufSetStatic(&gpu->vendor, FF_GPU_VENDOR_NAME_APPLE); gpu->type = FF_GPU_TYPE_INTEGRATED; + } + else if (ffStrbufContainS(&gpu->name, "Intel")) + ffStrbufSetStatic(&gpu->vendor, FF_GPU_VENDOR_NAME_INTEL); + else if (ffStrbufContainS(&gpu->name, "AMD") || ffStrbufContainS(&gpu->name, "ATI")) + ffStrbufSetStatic(&gpu->vendor, FF_GPU_VENDOR_NAME_AMD); + else if (ffStrbufContainS(&gpu->name, "NVIDIA")) + ffStrbufSetStatic(&gpu->vendor, FF_GPU_VENDOR_NAME_NVIDIA); + } ffStrbufDestroy(&result.version); @@ -83,20 +79,26 @@ const char* detectByOpenGL(FFlist* gpus) const char* ffDetectGPU(const FFGPUOptions* options, FFlist* result) { - if (!options->forceVulkan) + if (options->detectionMethod <= FF_GPU_DETECTION_METHOD_PCI) { const char* error = ffDetectGPUImpl(options, result); if (!error && result->length > 0) return NULL; } - FFVulkanResult* vulkan = ffDetectVulkan(); - if (!vulkan->error && vulkan->gpus.length > 0) + if (options->detectionMethod <= FF_GPU_DETECTION_METHOD_VULKAN) + { + FFVulkanResult* vulkan = ffDetectVulkan(); + if (!vulkan->error && vulkan->gpus.length > 0) + { + ffListDestroy(result); + ffListInitMove(result, &vulkan->gpus); + return NULL; + } + } + if (options->detectionMethod <= FF_GPU_DETECTION_METHOD_OPENGL) { - ffListDestroy(result); - ffListInitMove(result, &vulkan->gpus); - return NULL; + if (detectByOpenGL(result) == NULL) + return NULL; } - if (detectByOpenGL(result) == NULL) - return NULL; return "GPU detection failed"; } diff --git a/src/detection/gpu/gpu_apple.m b/src/detection/gpu/gpu_apple.m index 85d951724..c63abaf5f 100644 --- a/src/detection/gpu/gpu_apple.m +++ b/src/detection/gpu/gpu_apple.m @@ -5,10 +5,14 @@ #ifndef MAC_OS_VERSION_13_0 #define MTLGPUFamilyMetal3 ((MTLGPUFamily) 5001) #endif +#ifndef MAC_OS_X_VERSION_10_15 + #define MTLFeatureSet_macOS_GPUFamily1_v4 ((MTLFeatureSet) 10004) + #define MTLFeatureSet_macOS_GPUFamily2_v1 ((MTLFeatureSet) 10005) +#endif const char* ffGpuDetectMetal(FFlist* gpus) { - if (@available(macOS 10.15, *)) + if (@available(macOS 10.13, *)) { for (id device in MTLCopyAllDevices()) { @@ -23,6 +27,12 @@ } if (!gpu) continue; + #ifndef MAC_OS_X_VERSION_10_15 + if ([device supportsFeatureSet:MTLFeatureSet_macOS_GPUFamily2_v1]) + ffStrbufSetStatic(&gpu->platformApi, "Metal Feature Set 2"); + else if ([device supportsFeatureSet:MTLFeatureSet_macOS_GPUFamily1_v1]) + ffStrbufSetStatic(&gpu->platformApi, "Metal Feature Set 1"); + #else // MAC_OS_X_VERSION_10_15 if ([device supportsFamily:MTLGPUFamilyMetal3]) ffStrbufSetStatic(&gpu->platformApi, "Metal 3"); else if ([device supportsFamily:MTLGPUFamilyCommon3]) @@ -32,15 +42,16 @@ else if ([device supportsFamily:MTLGPUFamilyCommon1]) ffStrbufSetStatic(&gpu->platformApi, "Metal Common 1"); - if (device.hasUnifiedMemory) + gpu->type = device.hasUnifiedMemory ? FF_GPU_TYPE_INTEGRATED : FF_GPU_TYPE_DISCRETE; + #endif + + if (gpu->type == FF_GPU_TYPE_INTEGRATED) { - gpu->type = FF_GPU_TYPE_INTEGRATED; gpu->shared.total = device.recommendedMaxWorkingSetSize; gpu->shared.used = device.currentAllocatedSize; } else { - gpu->type = FF_GPU_TYPE_DISCRETE; gpu->dedicated.total = device.recommendedMaxWorkingSetSize; gpu->dedicated.used = device.currentAllocatedSize; } diff --git a/src/detection/gpu/gpu_linux.c b/src/detection/gpu/gpu_linux.c index 1d0c612da..9387ce4ef 100644 --- a/src/detection/gpu/gpu_linux.c +++ b/src/detection/gpu/gpu_linux.c @@ -15,8 +15,68 @@ #include -static void pciDetectDriver(FFGPUResult* gpu, FFstrbuf* pciDir, FFstrbuf* buffer) +#if __has_include() + #include + #define FF_HAVE_DRM_H 1 +#elif __has_include() + #include + #define FF_HAVE_DRM_H 1 +#endif + +#if FF_HAVE_DRM_H + #include + #include + + #if __aarch64__ && __has_include() + #include + #define FF_HAVE_ASAHI_DRM_H 1 + #endif +#endif + + +#if FF_HAVE_DRM_H +static const char* drmDetectDriver(FFGPUResult* gpu, FFstrbuf* pciDir, FFstrbuf* buffer, const char* drmKey) { + ffStrbufSetS(buffer, "/dev/dri/"); + ffStrbufAppendS(buffer, drmKey); + FF_AUTO_CLOSE_FD int fd = open(buffer->chars, O_RDONLY); + if (fd < 0) return "open(/dev/dri/drm_key) failed"; + + ffStrbufEnsureFixedLengthFree(&gpu->driver, 128); + drm_version_t version = { + .name = gpu->driver.chars, + .name_len = gpu->driver.allocated, + }; + if (ioctl(fd, DRM_IOCTL_VERSION, &version) < 0) return "ioctl(DRM_IOCTL_VERSION) failed"; + gpu->driver.length = (uint32_t) version.name_len; + gpu->driver.chars[gpu->driver.length] = '\0'; + + if (version.version_major || version.version_minor || version.version_patchlevel) + ffStrbufAppendF(&gpu->driver, " %d.%d.%d", version.version_major, version.version_minor, version.version_patchlevel); + else + { + ffStrbufAppendS(pciDir, "/driver/module/version"); + if (ffReadFileBuffer(pciDir->chars, buffer)) + { + ffStrbufTrimRightSpace(buffer); + ffStrbufAppendC(&gpu->driver, ' '); + ffStrbufAppend(&gpu->driver, buffer); + } + } + return NULL; +} +#endif + +static bool pciDetectDriver(FFGPUResult* gpu, FFstrbuf* pciDir, FFstrbuf* buffer, FF_MAYBE_UNUSED const char* drmKey) +{ + #if FF_HAVE_DRM_H + if (drmKey) + { + drmDetectDriver(gpu, pciDir, buffer, drmKey); + if (gpu->driver.length > 0) return true; + } + #endif + ffStrbufAppendS(pciDir, "/driver"); char pathBuf[PATH_MAX]; ssize_t resultLength = readlink(pciDir->chars, pathBuf, sizeof(pathBuf)); @@ -35,8 +95,11 @@ static void pciDetectDriver(FFGPUResult* gpu, FFstrbuf* pciDir, FFstrbuf* buffer ffStrbufTrimRightSpace(buffer); ffStrbufAppendC(&gpu->driver, ' '); ffStrbufAppend(&gpu->driver, buffer); + return true; } } + + return false; } static void pciDetectAmdSpecific(const FFGPUOptions* options, FFGPUResult* gpu, FFstrbuf* pciDir, FFstrbuf* buffer) @@ -100,24 +163,26 @@ static void pciDetectAmdSpecific(const FFGPUOptions* options, FFGPUResult* gpu, static void pciDetectIntelSpecific(FFGPUResult* gpu, FFstrbuf* pciDir, FFstrbuf* buffer) { - if (!ffStrbufEndsWithS(pciDir, "/device")) // Must be in `/sys/class/drm/cardN/device` - return; - // Works for Intel GPUs // https://patchwork.kernel.org/project/intel-gfx/patch/1422039866-11572-3-git-send-email-ville.syrjala@linux.intel.com/ - ffStrbufSetNS(buffer, pciDir->length - (uint32_t) strlen("device"), pciDir->chars); - ffStrbufAppendS(buffer, "gt_max_freq_mhz"); - char str[16]; - ssize_t len = ffReadFileData(buffer->chars, sizeof(str) - 1, str); - if (len > 1) - { - str[len] = '\0'; - gpu->frequency = (double) strtoul(str, NULL, 10) / 1000.0; - } if (ffStrbufStartsWithS(&gpu->name, "Intel ")) ffStrbufSubstrAfter(&gpu->name, (uint32_t) strlen("Intel ")); gpu->type = ffStrbufStartsWithIgnCaseS(&gpu->name, "Arc ") ? FF_GPU_TYPE_DISCRETE : FF_GPU_TYPE_INTEGRATED; + + ffStrbufAppendS(pciDir, "/drm/"); + FF_AUTO_CLOSE_DIR DIR* dirp = opendir(pciDir->chars); + if (!dirp) return; + struct dirent* entry; + while ((entry = readdir(dirp)) != NULL) + { + if (ffStrStartsWith(entry->d_name, "card")) break; + } + if (!entry) return; + ffStrbufAppendS(pciDir, entry->d_name); + ffStrbufAppendS(pciDir, "/gt_max_freq_mhz"); + if (ffReadFileBuffer(pciDir->chars, buffer)) + gpu->frequency = ffStrbufToDouble(buffer) / 1000.0; } static bool loadPciIds(FFstrbuf* pciids) @@ -143,9 +208,9 @@ static bool loadPciIds(FFstrbuf* pciids) return false; } -static const char* detectPci(const FFGPUOptions* options, FFlist* gpus, FFstrbuf* buffer, FFstrbuf* drmDir) +static const char* detectPci(const FFGPUOptions* options, FFlist* gpus, FFstrbuf* buffer, FFstrbuf* deviceDir, const char* drmKey) { - const uint32_t drmDirPathLength = drmDir->length; + const uint32_t drmDirPathLength = deviceDir->length; uint32_t vendorId, deviceId, subVendorId, subDeviceId; uint8_t classId, subclassId; if (sscanf(buffer->chars + strlen("pci:"), "v%8" SCNx32 "d%8" SCNx32 "sv%8" SCNx32 "sd%8" SCNx32 "bc%2" SCNx8 "sc%2" SCNx8, &vendorId, &deviceId, &subVendorId, &subDeviceId, &classId, &subclassId) != 6) @@ -155,15 +220,23 @@ static const char* detectPci(const FFGPUOptions* options, FFlist* gpus, FFstrbuf return "Not a GPU device"; char pciPath[PATH_MAX]; - ssize_t pathLength = readlink(drmDir->chars, pciPath, sizeof(pciPath) - 1); - if (pathLength <= 0) - return "Unable to get PCI device path"; - pciPath[pathLength] = '\0'; - const char* pPciPath = strrchr(pciPath, '/'); - if (pPciPath) - pPciPath++; + const char* pPciPath = NULL; + if (drmKey) + { + ssize_t pathLength = readlink(deviceDir->chars, pciPath, sizeof(pciPath) - 1); + if (pathLength <= 0) + return "Unable to get PCI device path"; + pciPath[pathLength] = '\0'; + pPciPath = strrchr(pciPath, '/'); + if (__builtin_expect(pPciPath != NULL, true)) + pPciPath++; + else + pPciPath = pciPath; + } else - pPciPath = pciPath; + { + pPciPath = memrchr(deviceDir->chars, '/', deviceDir->length) + 1; + } uint32_t pciDomain, pciBus, pciDevice, pciFunc; if (sscanf(pPciPath, "%" SCNx32 ":%" SCNx32 ":%" SCNx32 ".%" SCNx32, &pciDomain, &pciBus, &pciDevice, &pciFunc) != 4) @@ -181,10 +254,12 @@ static const char* detectPci(const FFGPUOptions* options, FFlist* gpus, FFstrbuf gpu->deviceId = ((uint64_t) pciDomain << 6) | ((uint64_t) pciBus << 4) | (deviceId << 2) | pciFunc; gpu->frequency = FF_GPU_FREQUENCY_UNSET; + if (drmKey) ffStrbufSetF(&gpu->platformApi, "DRM (%s)", drmKey); + if (gpu->vendor.chars == FF_GPU_VENDOR_NAME_AMD) { - ffStrbufAppendS(drmDir, "/revision"); - if (ffReadFileBuffer(drmDir->chars, buffer)) + ffStrbufAppendS(deviceDir, "/revision"); + if (ffReadFileBuffer(deviceDir->chars, buffer)) { char* pend; uint64_t revision = strtoul(buffer->chars, &pend, 16); @@ -199,7 +274,7 @@ static const char* detectPci(const FFGPUOptions* options, FFlist* gpus, FFstrbuf #endif } } - ffStrbufSubstrBefore(drmDir, drmDirPathLength); + ffStrbufSubstrBefore(deviceDir, drmDirPathLength); } if (gpu->name.length == 0) @@ -213,18 +288,18 @@ static const char* detectPci(const FFGPUOptions* options, FFlist* gpus, FFstrbuf ffGPUParsePciIds(&pciids, subclassId, (uint16_t) vendorId, (uint16_t) deviceId, gpu); } - pciDetectDriver(gpu, drmDir, buffer); - ffStrbufSubstrBefore(drmDir, drmDirPathLength); + pciDetectDriver(gpu, deviceDir, buffer, drmKey); + ffStrbufSubstrBefore(deviceDir, drmDirPathLength); if (gpu->vendor.chars == FF_GPU_VENDOR_NAME_AMD) { - pciDetectAmdSpecific(options, gpu, drmDir, buffer); - ffStrbufSubstrBefore(drmDir, drmDirPathLength); + pciDetectAmdSpecific(options, gpu, deviceDir, buffer); + ffStrbufSubstrBefore(deviceDir, drmDirPathLength); } else if (gpu->vendor.chars == FF_GPU_VENDOR_NAME_INTEL) { - pciDetectIntelSpecific(gpu, drmDir, buffer); - ffStrbufSubstrBefore(drmDir, drmDirPathLength); + pciDetectIntelSpecific(gpu, deviceDir, buffer); + ffStrbufSubstrBefore(deviceDir, drmDirPathLength); } else if (gpu->vendor.chars == FF_GPU_VENDOR_NAME_NVIDIA) { @@ -261,7 +336,7 @@ static const char* detectPci(const FFGPUOptions* options, FFlist* gpus, FFstrbuf return NULL; } -FF_MAYBE_UNUSED static const char* detectAsahi(FFlist* gpus, FFstrbuf* buffer, FFstrbuf* drmDir) +FF_MAYBE_UNUSED static const char* detectAsahi(FFlist* gpus, FFstrbuf* buffer, FFstrbuf* drmDir, const char* drmKey) { uint32_t index = ffStrbufFirstIndexS(buffer, "apple,agx-t"); if (index == buffer->length) return "display-subsystem?"; @@ -272,14 +347,34 @@ FF_MAYBE_UNUSED static const char* detectAsahi(FFlist* gpus, FFstrbuf* buffer, F ffStrbufInitStatic(&gpu->name, ffCPUAppleCodeToName((uint32_t) gpu->deviceId)); ffStrbufInitStatic(&gpu->vendor, FF_GPU_VENDOR_NAME_APPLE); ffStrbufInit(&gpu->driver); - ffStrbufInit(&gpu->platformApi); + ffStrbufInitF(&gpu->platformApi, "DRM (%s)", drmKey); gpu->temperature = FF_GPU_TEMP_UNSET; gpu->coreCount = FF_GPU_CORE_COUNT_UNSET; gpu->type = FF_GPU_TYPE_INTEGRATED; gpu->dedicated.total = gpu->dedicated.used = gpu->shared.total = gpu->shared.used = FF_GPU_VMEM_SIZE_UNSET; gpu->frequency = FF_GPU_FREQUENCY_UNSET; - pciDetectDriver(gpu, drmDir, buffer); + pciDetectDriver(gpu, drmDir, buffer, drmKey); + + #if FF_HAVE_ASAHI_DRM_H + ffStrbufSetS(buffer, "/dev/dri/"); + ffStrbufAppendS(buffer, drmKey); + FF_AUTO_CLOSE_FD int fd = open(buffer->chars, O_RDONLY); + if (fd >= 0) + { + struct drm_asahi_params_global paramsGlobal = {}; + if (ioctl(fd, DRM_IOCTL_ASAHI_GET_PARAMS, &(struct drm_asahi_get_params){ + .param_group = DRM_ASAHI_GET_PARAMS, + .pointer = (uint64_t) ¶msGlobal, + .size = sizeof(paramsGlobal), + }) >= 0) + { + gpu->coreCount = (int) paramsGlobal.num_cores_total_active; + gpu->frequency = paramsGlobal.max_frequency_khz / 1e6; + gpu->deviceId = paramsGlobal.chip_id; + } + } + #endif return NULL; } @@ -311,10 +406,10 @@ static const char* drmDetectGPUs(const FFGPUOptions* options, FFlist* gpus) ffStrbufSubstrBefore(&drmDir, drmDir.length - (uint32_t) strlen("/modalias")); if (ffStrbufStartsWithS(&buffer, "pci:")) - detectPci(options, gpus, &buffer, &drmDir); + detectPci(options, gpus, &buffer, &drmDir, entry->d_name); #ifdef __aarch64__ else if (ffStrbufStartsWithS(&buffer, "of:")) - detectAsahi(gpus, &buffer, &drmDir); + detectAsahi(gpus, &buffer, &drmDir, entry->d_name); #endif ffStrbufSubstrBefore(&drmDir, drmDirLength); @@ -356,7 +451,7 @@ static const char* pciDetectGPUs(const FFGPUOptions* options, FFlist* gpus) ffStrbufSubstrBefore(&pciDir, pciDevDirLength); assert(ffStrbufStartsWithS(&buffer, "pci:")); - detectPci(options, gpus, &buffer, &pciDir); + detectPci(options, gpus, &buffer, &pciDir, NULL); ffStrbufSubstrBefore(&pciDir, pciBaseDirLength); } @@ -365,14 +460,16 @@ static const char* pciDetectGPUs(const FFGPUOptions* options, FFlist* gpus) const char* ffDetectGPUImpl(const FFGPUOptions* options, FFlist* gpus) { - #ifdef FF_HAVE_DIRECTX_HEADERS - const char* ffGPUDetectByDirectX(const FFGPUOptions* options, FFlist* gpus); - if (ffGPUDetectByDirectX(options, gpus) == NULL) - return NULL; - #endif - - if (drmDetectGPUs(options, gpus) == NULL && gpus->length > 0) - return NULL; + if (options->detectionMethod == FF_GPU_DETECTION_METHOD_AUTO) + { + #ifdef FF_HAVE_DIRECTX_HEADERS + const char* ffGPUDetectByDirectX(const FFGPUOptions* options, FFlist* gpus); + if (ffGPUDetectByDirectX(options, gpus) == NULL) + return NULL; + #endif + if (drmDetectGPUs(options, gpus) == NULL && gpus->length > 0) + return NULL; + } return pciDetectGPUs(options, gpus); } diff --git a/src/detection/monitor/monitor_apple.m b/src/detection/monitor/monitor_apple.m index 348a1ad33..31b7ab9e3 100644 --- a/src/detection/monitor/monitor_apple.m +++ b/src/detection/monitor/monitor_apple.m @@ -9,11 +9,22 @@ extern CFDictionaryRef CoreDisplay_DisplayCreateInfoDictionary(CGDirectDisplayID display) __attribute__((weak_import)); +#ifndef MAC_OS_X_VERSION_10_15 +#import +extern CFDictionaryRef CoreDisplay_IODisplayCreateInfoDictionary(io_service_t framebuffer, IOOptionBits options) __attribute__((weak_import)); +#endif + static bool detectHdrSupportWithNSScreen(FFDisplayResult* display) { NSScreen* mainScreen = NSScreen.mainScreen; if (display->primary) + { + #ifdef MAC_OS_X_VERSION_10_15 return mainScreen.maximumPotentialExtendedDynamicRangeColorComponentValue > 1; + #else + return mainScreen.maximumExtendedDynamicRangeColorComponentValue > 1; + #endif + } else { for (NSScreen* screen in NSScreen.screens) @@ -22,7 +33,11 @@ static bool detectHdrSupportWithNSScreen(FFDisplayResult* display) NSNumber* screenNumber = [screen.deviceDescription valueForKey:@"NSScreenNumber"]; if (screenNumber && screenNumber.longValue == (long) display->id) { + #ifdef MAC_OS_X_VERSION_10_15 return screen.maximumPotentialExtendedDynamicRangeColorComponentValue > 1; + #else + return screen.maximumExtendedDynamicRangeColorComponentValue > 1; + #endif } } } @@ -31,13 +46,19 @@ static bool detectHdrSupportWithNSScreen(FFDisplayResult* display) const char* ffDetectMonitor(FFlist* results) { + #ifdef MAC_OS_X_VERSION_10_15 if(!CoreDisplay_DisplayCreateInfoDictionary) return "CoreDisplay_DisplayCreateInfoDictionary is not available"; - + #endif const FFDisplayServerResult* displayServer = ffConnectDisplayServer(); FF_LIST_FOR_EACH(FFDisplayResult, display, displayServer->displays) { + #ifdef MAC_OS_X_VERSION_10_15 CFDictionaryRef FF_CFTYPE_AUTO_RELEASE displayInfo = CoreDisplay_DisplayCreateInfoDictionary((CGDirectDisplayID) display->id); + #else + io_service_t servicePort = CGDisplayIOServicePort((CGDirectDisplayID) display->id); + CFDictionaryRef FF_CFTYPE_AUTO_RELEASE displayInfo = CoreDisplay_IODisplayCreateInfoDictionary(servicePort, kIODisplayOnlyPreferredName); + #endif if(!displayInfo) continue; bool isVirtual = false; diff --git a/src/detection/opencl/opencl.c b/src/detection/opencl/opencl.c index 957c1c1dc..f3339bee8 100644 --- a/src/detection/opencl/opencl.c +++ b/src/detection/opencl/opencl.c @@ -1,6 +1,13 @@ #include "detection/opencl/opencl.h" -#if defined(FF_HAVE_OPENCL) || defined(__APPLE__) +#ifdef __APPLE__ +#include +#if (MAC_OS_X_VERSION_MIN_REQUIRED > 1050) && !defined(__ppc__) +#define MACOS_HAS_OPENCL +#endif +#endif + +#if defined(FF_HAVE_OPENCL) || defined(MACOS_HAS_OPENCL) #include "common/library.h" #include "common/parsing.h" @@ -64,7 +71,7 @@ static const char* openCLHandleData(OpenCLData* data, FFOpenCLResult* result) return NULL; } -#endif // defined(FF_HAVE_OPENCL) || defined(__APPLE__) +#endif // defined(FF_HAVE_OPENCL) || defined(MACOS_HAS_OPENCL) const char* ffDetectOpenCL(FFOpenCLResult* result) { @@ -84,7 +91,7 @@ const char* ffDetectOpenCL(FFOpenCLResult* result) return openCLHandleData(&data, result); - #elif defined(__APPLE__) // FF_HAVE_OPENCL + #elif defined(MACOS_HAS_OPENCL) // FF_HAVE_OPENCL OpenCLData data; data.ffclGetPlatformIDs = clGetPlatformIDs; diff --git a/src/detection/os/os_apple.m b/src/detection/os/os_apple.m index dbb55539e..d08aed435 100644 --- a/src/detection/os/os_apple.m +++ b/src/detection/os/os_apple.m @@ -97,8 +97,7 @@ void ffDetectOSImpl(FFOSResult* os) { parseSystemVersion(os); - if(ffStrbufStartsWithIgnCaseS(&os->name, "MacOS")) - ffStrbufSetStatic(&os->id, "macos"); + ffStrbufSetStatic(&os->id, "macos"); if(os->version.length == 0) ffSysctlGetString("kern.osproductversion", &os->version); diff --git a/src/detection/os/os_linux.c b/src/detection/os/os_linux.c index 2e8d3bc92..a4ea2f5cf 100644 --- a/src/detection/os/os_linux.c +++ b/src/detection/os/os_linux.c @@ -31,7 +31,7 @@ static bool parseLsbRelease(const char* fileName, FFOSResult* result) static bool parseOsRelease(const char* fileName, FFOSResult* result) { - return ffParsePropFileValues(fileName, 10, (FFpropquery[]) { + return ffParsePropFileValues(fileName, 11, (FFpropquery[]) { {"PRETTY_NAME =", &result->prettyName}, {"NAME =", &result->name}, {"ID =", &result->id}, @@ -41,7 +41,8 @@ static bool parseOsRelease(const char* fileName, FFOSResult* result) {"VERSION =", &result->version}, {"VERSION_ID =", &result->versionID}, {"VERSION_CODENAME =", &result->codename}, - {"BUILD_ID =", &result->buildID} + {"CODENAME =", &result->codename}, + {"BUILD_ID =", &result->buildID}, }); } @@ -156,6 +157,12 @@ static bool detectDebianDerived(FFOSResult* result) ffStrbufSetNS(&result->versionID, versionEnd - versionStart, result->prettyName.chars + versionStart); return true; } + else if (ffStrbufStartsWithS(&result->name, "Loc-OS")) + { + ffStrbufSetS(&result->id, "locos"); + ffStrbufSetS(&result->idLike, "debian"); + return true; + } else if (ffPathExists("/usr/bin/pveversion", FF_PATHTYPE_FILE)) { ffStrbufSetS(&result->id, "pve"); diff --git a/src/detection/packages/packages.h b/src/detection/packages/packages.h index d8983242a..4e954b96a 100644 --- a/src/detection/packages/packages.h +++ b/src/detection/packages/packages.h @@ -14,6 +14,8 @@ typedef struct FFPackagesResult uint32_t eopkg; uint32_t flatpakSystem; uint32_t flatpakUser; + uint32_t lpkg; + uint32_t lpkgbuild; uint32_t nixDefault; uint32_t nixSystem; uint32_t nixUser; diff --git a/src/detection/packages/packages_linux.c b/src/detection/packages/packages_linux.c index 0e19043a4..5933c87c2 100644 --- a/src/detection/packages/packages_linux.c +++ b/src/detection/packages/packages_linux.c @@ -412,6 +412,7 @@ static void getPackageCounts(FFstrbuf* baseDir, FFPackagesResult* packageCounts, { if (!(options->disabled & FF_PACKAGES_FLAG_APK_BIT)) packageCounts->apk += getNumStrings(baseDir, "/lib/apk/db/installed", "C:Q"); if (!(options->disabled & FF_PACKAGES_FLAG_DPKG_BIT)) packageCounts->dpkg += getNumStrings(baseDir, "/var/lib/dpkg/status", "Status: install ok installed"); + if (!(options->disabled & FF_PACKAGES_FLAG_LPKG_BIT)) packageCounts->lpkg += getNumStrings(baseDir, "/opt/Loc-OS-LPKG/installed-lpkg/Listinstalled-lpkg.list", "\n"); if (!(options->disabled & FF_PACKAGES_FLAG_EMERGE_BIT)) packageCounts->emerge += countFilesRecursive(baseDir, "/var/db/pkg", "SIZE"); if (!(options->disabled & FF_PACKAGES_FLAG_EOPKG_BIT)) packageCounts->eopkg += getNumElements(baseDir, "/var/lib/eopkg/package", DT_DIR); if (!(options->disabled & FF_PACKAGES_FLAG_FLATPAK_BIT)) packageCounts->flatpakSystem += getFlatpak(baseDir, "/var/lib/flatpak"); @@ -421,6 +422,7 @@ static void getPackageCounts(FFstrbuf* baseDir, FFPackagesResult* packageCounts, packageCounts->nixSystem += getNixPackages(baseDir, "/run/current-system"); } if (!(options->disabled & FF_PACKAGES_FLAG_PACMAN_BIT)) packageCounts->pacman += getNumElements(baseDir, "/var/lib/pacman/local", DT_DIR); + if (!(options->disabled & FF_PACKAGES_FLAG_LPKGBUILD_BIT)) packageCounts->lpkgbuild += getNumElements(baseDir, "/opt/Loc-OS-LPKG/lpkgbuild/remove", DT_REG); if (!(options->disabled & FF_PACKAGES_FLAG_PKGTOOL_BIT)) packageCounts->pkgtool += getNumElements(baseDir, "/var/log/packages", DT_REG); if (!(options->disabled & FF_PACKAGES_FLAG_RPM_BIT)) packageCounts->rpm += getSQLite3Int(baseDir, "/var/lib/rpm/rpmdb.sqlite", "SELECT count(*) FROM Packages"); if (!(options->disabled & FF_PACKAGES_FLAG_SNAP_BIT)) packageCounts->snap += getSnap(baseDir); @@ -508,30 +510,28 @@ void ffDetectPackagesImpl(FFPackagesResult* result, FFPackagesOptions* options) ffStrbufAppendS(&profilePath, ".nix-profile"); if (ffPathExists(profilePath.chars, FF_PATHTYPE_DIRECTORY)) { - result->nixUser = getNixPackages(&baseDir, ".nix-profile"); + result->nixUser += getNixPackages(&baseDir, ".nix-profile"); } + // check if $XDG_STATE_HOME/nix/profile exists + FF_STRBUF_AUTO_DESTROY stateDir = ffStrbufCreate(); + const char* stateHome = getenv("XDG_STATE_HOME"); + if(ffStrSet(stateHome)) + { + ffStrbufSetS(&stateDir, stateHome); + ffStrbufEnsureEndsWithC(&stateDir, '/'); + } else { - FF_STRBUF_AUTO_DESTROY stateDir = ffStrbufCreate(); - const char* stateHome = getenv("XDG_STATE_HOME"); - if(ffStrSet(stateHome)) - { - ffStrbufSetS(&stateDir, stateHome); - ffStrbufEnsureEndsWithC(&stateDir, '/'); - } - else - { - ffStrbufSet(&stateDir, &instance.state.platform.homeDir); - ffStrbufAppendS(&stateDir, ".local/state/"); - } - - ffStrbufSet(&profilePath, &stateDir); - ffStrbufAppendS(&profilePath, "nix/profile"); - result->nixUser = getNixPackages(&stateDir, "nix/profile"); + ffStrbufSet(&stateDir, &instance.state.platform.homeDir); + ffStrbufAppendS(&stateDir, ".local/state/"); } + + ffStrbufSet(&profilePath, &stateDir); + ffStrbufAppendS(&profilePath, "nix/profile"); + result->nixUser += getNixPackages(&stateDir, "nix/profile"); } - + if (!(options->disabled & FF_PACKAGES_FLAG_FLATPAK_BIT)) result->flatpakUser = getFlatpak(&baseDir, "/.local/share/flatpak"); } diff --git a/src/detection/physicaldisk/physicaldisk_apple.c b/src/detection/physicaldisk/physicaldisk_apple.c index 8f4a11420..b60e466d3 100644 --- a/src/detection/physicaldisk/physicaldisk_apple.c +++ b/src/detection/physicaldisk/physicaldisk_apple.c @@ -7,17 +7,22 @@ #include #include #include -#include +#ifdef MAC_OS_X_VERSION_10_15 + #include +#endif +#ifdef MAC_OS_X_VERSION_10_15 static inline void wrapIoDestroyPlugInInterface(IOCFPlugInInterface*** pluginInf) { assert(pluginInf); if (*pluginInf) IODestroyPlugInInterface(*pluginInf); } +#endif static const char* detectSsdTemp(io_service_t entryPhysical, double* temp) { + #ifdef MAC_OS_X_VERSION_10_15 __attribute__((__cleanup__(wrapIoDestroyPlugInInterface))) IOCFPlugInInterface** pluginInf = NULL; int32_t score; if (IOCreatePlugInInterfaceForService(entryPhysical, kIONVMeSMARTUserClientTypeID, kIOCFPlugInInterfaceID, &pluginInf, &score) != kIOReturnSuccess) @@ -36,6 +41,9 @@ static const char* detectSsdTemp(io_service_t entryPhysical, double* temp) (*pluginInf)->Release(smartInf); return error; + #else + return "No support for old MacOS version"; + #endif } const char* ffDetectPhysicalDisk(FFlist* result, FFPhysicalDiskOptions* options) @@ -117,12 +125,14 @@ const char* ffDetectPhysicalDisk(FFlist* result, FFPhysicalDiskOptions* options) } } + #ifdef MAC_OS_X_VERSION_10_15 if (options->temp) { FF_CFTYPE_AUTO_RELEASE CFBooleanRef nvmeSMARTCapable = IORegistryEntryCreateCFProperty(entryPhysical, CFSTR(kIOPropertyNVMeSMARTCapableKey), kCFAllocatorDefault, kNilOptions); if (nvmeSMARTCapable && CFBooleanGetValue(nvmeSMARTCapable)) detectSsdTemp(entryPhysical, &device->temperature); } + #endif } } diff --git a/src/detection/terminalshell/terminalshell_linux.c b/src/detection/terminalshell/terminalshell_linux.c index 006f40a37..77fd23c2a 100644 --- a/src/detection/terminalshell/terminalshell_linux.c +++ b/src/detection/terminalshell/terminalshell_linux.c @@ -56,6 +56,10 @@ static void getProcessInformation(pid_t pid, FFstrbuf* processName, FFstrbuf* ex int mibs[] = { CTL_KERN, KERN_PROCARGS2, pid }; if (sysctl(mibs, sizeof(mibs) / sizeof(*mibs), NULL, &len, NULL, 0) == 0) { + #ifndef MAC_OS_X_VERSION_10_15 + //don't know why if don't let len longer, proArgs2 and len will change during the following sysctl() in old MacOS version. + len++; + #endif FF_AUTO_FREE char* const procArgs2 = malloc(len); if (sysctl(mibs, sizeof(mibs) / sizeof(*mibs), procArgs2, &len, NULL, 0) == 0) { @@ -244,6 +248,8 @@ static pid_t getShellInfo(FFShellResult* result, pid_t pid) ffStrEquals(name, "guake-wrapped") || ffStrEquals(name, "time") || ffStrEquals(name, "hyfetch") || //when hyfetch uses fastfetch as backend + ffStrEquals(name, "clifm") || //https://github.com/leo-arch/clifm/issues/289 + ffStrEquals(name, "valgrind") || ffStrContainsIgnCase(name, "debug") || ffStrContainsIgnCase(name, "not-found") || ffStrEndsWith(name, ".sh") @@ -294,6 +300,7 @@ static pid_t getTerminalInfo(FFTerminalResult* result, pid_t pid) ffStrEquals(name, "xonsh") || // works in Linux but not in macOS because kernel returns `Python` in this case ffStrEquals(name, "login") || ffStrEquals(name, "sshd") || + ffStrEquals(name, "clifm") || // https://github.com/leo-arch/clifm/issues/289 ffStrEquals(name, "chezmoi") || // #762 #ifdef __linux__ ffStrStartsWith(name, "flatpak-") || // #707 diff --git a/src/detection/wifi/wifi_apple.m b/src/detection/wifi/wifi_apple.m index 64d90ccb2..8fdb40637 100644 --- a/src/detection/wifi/wifi_apple.m +++ b/src/detection/wifi/wifi_apple.m @@ -135,13 +135,13 @@ case kCWSecurityEnterprise: ffStrbufSetStatic(&item->conn.security, "Enterprise"); break; - case kCWSecurityWPA3Personal: + case 11 /*kCWSecurityWPA3Personal*/: ffStrbufSetStatic(&item->conn.security, "WPA3 Personal"); break; - case kCWSecurityWPA3Enterprise: + case 12 /*kCWSecurityWPA3Enterprise*/: ffStrbufSetStatic(&item->conn.security, "WPA3 Enterprise"); break; - case kCWSecurityWPA3Transition: + case 13 /*kCWSecurityWPA3Transition*/: ffStrbufSetStatic(&item->conn.security, "WPA3 Transition"); break; case 14 /*kCWSecurityOWE*/: diff --git a/src/detection/wifi/wifi_bsd.c b/src/detection/wifi/wifi_bsd.c index 663c6a4be..66edc10c6 100644 --- a/src/detection/wifi/wifi_bsd.c +++ b/src/detection/wifi/wifi_bsd.c @@ -36,9 +36,17 @@ const char* ffDetectWifi(FFlist* result) item->conn.txRate = 0.0/0.0; ffParsePropLines(ifconfig.chars, "ssid ", &item->conn.ssid); - if (item->conn.ssid.length) ffStrbufSubstrBeforeFirstC(&item->conn.ssid, ' '); + if (item->conn.ssid.length) + { + uint32_t ibssid = ffStrbufFirstIndexS(&item->conn.ssid, " bssid "); + if (ibssid < item->conn.ssid.length) + { + ibssid += (uint32_t) strlen(" bssid "); + ffStrbufSetS(&item->conn.macAddress, item->conn.ssid.chars + ibssid); + } - ffParsePropLines(ifconfig.chars, "ether ", &item->conn.macAddress); + ffStrbufSubstrBeforeFirstC(&item->conn.ssid, ' '); + } ffParsePropLines(ifconfig.chars, "media: ", &item->conn.protocol); if (item->conn.protocol.length) @@ -53,7 +61,7 @@ const char* ffDetectWifi(FFlist* result) } } - ffParsePropLines(ifconfig.chars, "status: ", &item->conn.macAddress); + ffParsePropLines(ifconfig.chars, "status: ", &item->conn.status); } } diff --git a/src/fastfetch.c b/src/fastfetch.c index 722c61462..cfe2d6185 100644 --- a/src/fastfetch.c +++ b/src/fastfetch.c @@ -234,10 +234,13 @@ static bool printSpecificCommandHelp(const char* command) } } + yyjson_doc_free(doc); return true; } } } + + yyjson_doc_free(doc); return false; } diff --git a/src/logo/ascii/furreto.txt b/src/logo/ascii/furreto.txt new file mode 100644 index 000000000..14ba1c1d0 --- /dev/null +++ b/src/logo/ascii/furreto.txt @@ -0,0 +1,22 @@ + .$2xOOko $1.$2odd, + oX$1WW$2KOOOO. 'ON$1WW$20kkk. + $1.$2k0XKOOOOOOcOON$1W$2NOOOOO. + xOOOOOOOOOkkOOOOOOOOO; + $1.$2O0OkkocxO000000kcdk0OO0OOkx + k$1W$1M$2Xkkkkloxkkkx; :dxxxxddc... + 'kO0OOOOOkc .cl:..kk0KK0Okc + ;kOOO0000xd. dO00000Oo .xkO$1NMM$2XOOOO +.dddxkOOOkddc.kKN$1WW$2N000000l.ddk0000OOOO. + 'dd:;ddddd;.dK$1MMMW$2K00KKK0O::ddxkO00Oko + .okxkOKK0kkOO00KKOxxlodddddddl + .00OOkkkkkkkkOOO00OOOO0O; .dddl + 'kkkkkxxkkkkkkkOOkxdxkxxddd. + cddddddddxxkkkkkxddddddddddo + 'ddddddodddddddddddddddddddc + $1.$2ddddddodddddddddodddddddc + .odddo. + + $1.$2kOOkkk; + lkK$1WN$2kkkxc + kkxkkkkkkx. + ,,..xxx. diff --git a/src/logo/ascii/locos.txt b/src/logo/ascii/locos.txt new file mode 100644 index 000000000..cbcb5be8a --- /dev/null +++ b/src/logo/ascii/locos.txt @@ -0,0 +1,18 @@ + $3..;:::::::;. + .0X'$1''''''''''''$3'N: + :Xd$1,.'''''''''''''''$3lKx + .0o$1'.'''''''''''''''''''$3:K; + .O$1;.'$3okOx:$1'''''''''$3,dOOx:$1''$3O; + xc$1.'$3xXl$1 $3kX;$1''$2:cc;$1''$3kXl$1 $3OX;$1''$3k. + 'x$1..'$3:0X0Xx$1',$2OOOOOd$1,:$30X0Xx$1'''$3o: + ::$1..'$2,clollxOOOOOOOOdlddlc$1''''$3d + l'$1..$2':kkkkOOOOOOOOOOOOkkkx,$1'''$3x + o$1...''''',$2codxkkxxxdl$1:''''''''$3d. + d$1...''''''''''''''''''''''''''$3o. + d$1..''''$3,loc,$1''''''''$3;os:,$1'''''$3d' + :d$1..'$3,dKXXXXX0kxxxk0XXXXXX0l$1'''$3,O. + '0$1..'$3cKXXXXXXXXXXXXXXXXXXXXXXk$1'''$3ck + '0$1'.'$3cXXXXXXXXXXXXXXXXXXXXXXXXXO$1'''$3od + .0$1,.''$30XXXXXXXXXXXXXXXXXXXXXXXXXXo$1'''$3k; + cc$1..''$3XXXXXXXXXXXXXXXXXXXXXXXXXXXk$1''''$3k + l$1...',$3XXXXXXXXXXXXXXXXXXXXXXXXXXXO$1''''$3d. diff --git a/src/logo/ascii/spoinkos.txt b/src/logo/ascii/spoinkos.txt new file mode 100644 index 000000000..054b004bf --- /dev/null +++ b/src/logo/ascii/spoinkos.txt @@ -0,0 +1,5 @@ + @..@ + (----) + ( >__< ) + ^^ ~~ ^^ +| Spoink! | diff --git a/src/logo/builtin.c b/src/logo/builtin.c index be92989e6..bcc2549bb 100644 --- a/src/logo/builtin.c +++ b/src/logo/builtin.c @@ -1558,18 +1558,6 @@ static const FFlogo F[] = { .colorKeys = FF_COLOR_FG_BLUE, .colorTitle = FF_COLOR_FG_BLUE, }, - // FedoraOnyx placeholder - Currently does not have a logo - //{ - //.names = {"Fedora_onyx", "fedora-onyx", "fedora-linux-onyx", "fedora-linux_onyx"}, - //.type = FF_LOGO_LINE_TYPE_ALTER_BIT, - //.lines = FASTFETCH_DATATEXT_LOGO_FEDORA_ONYX, - //.colors = { - //FF_COLOR_FG_BLUE, - //FF_COLOR_FG_WHITE, - //}, - //.colorKeys = FF_COLOR_FG_BLUE, - //.colorTitle = FF_COLOR_FG_BLUE, - //}, // FedoraCoreOS { .names = {"Fedora_coreos", "fedora-coreos", "fedora-linux-coreos", "fedora-linux_coreos"}, @@ -1678,6 +1666,17 @@ static const FFlogo F[] = { .colorKeys = FF_COLOR_FG_MAGENTA, .colorTitle = FF_COLOR_FG_WHITE, }, + // Furreto + { + .names = {"Furreto"}, + .lines = FASTFETCH_DATATEXT_LOGO_FURRETO, + .colors = { + FF_COLOR_FG_WHITE, + FF_COLOR_FG_LIGHT_MAGENTA, + }, + .colorKeys = FF_COLOR_FG_CYAN, + .colorTitle = FF_COLOR_FG_CYAN, + }, // LAST {}, }; @@ -2121,13 +2120,11 @@ static const FFlogo K[] = { }, // KDENeon { - .names = {"KDE", "kde-neon"}, + .names = {"KDE", "kde-neon", "kde neon"}, .lines = FASTFETCH_DATATEXT_LOGO_KDE, .colors = { FF_COLOR_FG_GREEN, }, - .colorKeys = FF_COLOR_FG_GREEN, - .colorTitle = FF_COLOR_FG_GREEN, }, // Kibojoe { @@ -2385,6 +2382,18 @@ static const FFlogo L[] = { FF_COLOR_FG_WHITE, }, }, + // Loc-OS + { + .names = {"loc-os", "Loc-OS Linux"}, + .lines = FASTFETCH_DATATEXT_LOGO_LOCOS, + .colors = { + FF_COLOR_FG_BLACK, + FF_COLOR_FG_YELLOW, + FF_COLOR_FG_WHITE, + }, + .colorKeys = FF_COLOR_FG_YELLOW, + .colorTitle = FF_COLOR_FG_RED, + }, // Lunar { .names = {"Lunar"}, @@ -2542,7 +2551,7 @@ static const FFlogo M[] = { }, // Manjaro { - .names = {"manjaro", "manjaro-linux"}, + .names = {"manjaro", "manjaro-linux", "manjarolinux"}, .lines = FASTFETCH_DATATEXT_LOGO_MANJARO, .colors = { FF_COLOR_FG_GREEN, @@ -3821,6 +3830,14 @@ static const FFlogo S[] = { FF_COLOR_FG_YELLOW, }, }, + // SpoinkOS + { + .names = {"SpoinkOS", "spoink-os"}, + .lines = FASTFETCH_DATATEXT_LOGO_SPOINKOS, + .colors = { + FF_COLOR_FG_GREEN, + }, + }, // Slackel { .names = {"Slackel"}, @@ -4044,10 +4061,9 @@ static const FFlogo T[] = { .names = {"TorizonCore"}, .lines = FASTFETCH_DATATEXT_LOGO_TORIZONCORE, .colors = { + FF_COLOR_FG_LIGHT_WHITE, FF_COLOR_FG_YELLOW, - FF_COLOR_FG_BLUE, - FF_COLOR_FG_LIGHT_BLACK, - FF_COLOR_FG_MAGENTA, + FF_COLOR_FG_BLUE }, }, // Trisquel diff --git a/src/modules/bios/bios.c b/src/modules/bios/bios.c index b135b958b..3e9b73289 100644 --- a/src/modules/bios/bios.c +++ b/src/modules/bios/bios.c @@ -128,12 +128,6 @@ void ffGenerateBiosJsonResult(FF_MAYBE_UNUSED FFBiosOptions* options, yyjson_mut goto exit; } - if (bios.version.length == 0) - { - yyjson_mut_obj_add_str(doc, module, "error", "bios_version is not set."); - goto exit; - } - yyjson_mut_val* obj = yyjson_mut_obj_add_obj(doc, module, "result"); yyjson_mut_obj_add_strbuf(doc, obj, "date", &bios.date); yyjson_mut_obj_add_strbuf(doc, obj, "release", &bios.release); @@ -165,7 +159,7 @@ void ffInitBiosOptions(FFBiosOptions* options) ffOptionInitModuleBaseInfo( &options->moduleInfo, FF_BIOS_MODULE_NAME, - "Print BIOS name, version, release date, etc", + "Print information of 1st-stage bootloader (name, version, release date, etc)", ffParseBiosCommandOptions, ffParseBiosJsonObject, ffPrintBios, diff --git a/src/modules/bootmgr/bootmgr.c b/src/modules/bootmgr/bootmgr.c new file mode 100644 index 000000000..497c0d940 --- /dev/null +++ b/src/modules/bootmgr/bootmgr.c @@ -0,0 +1,143 @@ +#include "common/printing.h" +#include "common/jsonconfig.h" +#include "detection/bootmgr/bootmgr.h" +#include "modules/bootmgr/bootmgr.h" +#include "util/stringUtils.h" + +#define FF_BOOTMGR_NUM_FORMAT_ARGS 4 + +void ffPrintBootmgr(FFBootmgrOptions* options) +{ + FFBootmgrResult bootmgr = { + .name = ffStrbufCreate(), + .firmware = ffStrbufCreate(), + }; + + const char* error = ffDetectBootmgr(&bootmgr); + + if(error) + { + ffPrintError(FF_BOOTMGR_MODULE_NAME, 0, &options->moduleArgs, FF_PRINT_TYPE_DEFAULT, "%s", error); + return; + } + + FF_STRBUF_AUTO_DESTROY firmwareName = ffStrbufCreateCopy(&bootmgr.firmware); + #ifndef __APPLE__ + ffStrbufSubstrAfterLastC(&firmwareName, '\\'); + #else + ffStrbufSubstrAfterLastC(&firmwareName, '/'); + #endif + + if(options->moduleArgs.outputFormat.length == 0) + { + ffPrintLogoAndKey(FF_BOOTMGR_MODULE_NAME, 0, &options->moduleArgs, FF_PRINT_TYPE_DEFAULT); + ffStrbufWriteTo(&bootmgr.name, stdout); + if (firmwareName.length > 0) + printf(" - %s\n", firmwareName.chars); + else + putchar('\n'); + } + else + { + FF_PRINT_FORMAT_CHECKED(FF_BOOTMGR_MODULE_NAME, 0, &options->moduleArgs, FF_PRINT_TYPE_DEFAULT, FF_BOOTMGR_NUM_FORMAT_ARGS, ((FFformatarg[]) { + {FF_FORMAT_ARG_TYPE_STRBUF, &bootmgr.name}, + {FF_FORMAT_ARG_TYPE_STRBUF, &bootmgr.firmware}, + {FF_FORMAT_ARG_TYPE_STRBUF, &firmwareName}, + {FF_FORMAT_ARG_TYPE_BOOL, &bootmgr.secureBoot}, + })); + } + + ffStrbufDestroy(&bootmgr.name); + ffStrbufDestroy(&bootmgr.firmware); +} + +bool ffParseBootmgrCommandOptions(FFBootmgrOptions* options, const char* key, const char* value) +{ + const char* subKey = ffOptionTestPrefix(key, FF_BOOTMGR_MODULE_NAME); + if (!subKey) return false; + if (ffOptionParseModuleArgs(key, subKey, value, &options->moduleArgs)) + return true; + + return false; +} + +void ffParseBootmgrJsonObject(FFBootmgrOptions* options, yyjson_val* module) +{ + yyjson_val *key_, *val; + size_t idx, max; + yyjson_obj_foreach(module, idx, max, key_, val) + { + const char* key = yyjson_get_str(key_); + if(ffStrEqualsIgnCase(key, "type")) + continue; + + if (ffJsonConfigParseModuleArgs(key, val, &options->moduleArgs)) + continue; + + ffPrintError(FF_BOOTMGR_MODULE_NAME, 0, &options->moduleArgs, FF_PRINT_TYPE_DEFAULT, "Unknown JSON key %s", key); + } +} + +void ffGenerateBootmgrJsonConfig(FFBootmgrOptions* options, yyjson_mut_doc* doc, yyjson_mut_val* module) +{ + __attribute__((__cleanup__(ffDestroyBootmgrOptions))) FFBootmgrOptions defaultOptions; + ffInitBootmgrOptions(&defaultOptions); + + ffJsonConfigGenerateModuleArgsConfig(doc, module, &defaultOptions.moduleArgs, &options->moduleArgs); +} + +void ffGenerateBootmgrJsonResult(FF_MAYBE_UNUSED FFBootmgrOptions* options, yyjson_mut_doc* doc, yyjson_mut_val* module) +{ + FFBootmgrResult bootmgr = { + .name = ffStrbufCreate(), + .firmware = ffStrbufCreate(), + }; + + const char* error = ffDetectBootmgr(&bootmgr); + + if (error) + { + yyjson_mut_obj_add_str(doc, module, "error", error); + goto exit; + } + + yyjson_mut_val* obj = yyjson_mut_obj_add_obj(doc, module, "result"); + yyjson_mut_obj_add_strbuf(doc, obj, "name", &bootmgr.name); + yyjson_mut_obj_add_strbuf(doc, obj, "firmware", &bootmgr.firmware); + yyjson_mut_obj_add_bool(doc, obj, "secureBoot", bootmgr.secureBoot); + +exit: + ffStrbufDestroy(&bootmgr.name); + ffStrbufDestroy(&bootmgr.firmware); +} + +void ffPrintBootmgrHelpFormat(void) +{ + FF_PRINT_MODULE_FORMAT_HELP_CHECKED(FF_BOOTMGR_MODULE_NAME, "{4} ({2})", FF_BOOTMGR_NUM_FORMAT_ARGS, ((const char* []) { + "Name / description", + "Firmware file path", + "Firmware file name", + "Is secure boot enabled", + })); +} + +void ffInitBootmgrOptions(FFBootmgrOptions* options) +{ + ffOptionInitModuleBaseInfo( + &options->moduleInfo, + FF_BOOTMGR_MODULE_NAME, + "Print information of 2nd-stage bootloader (name, firmware, etc)", + ffParseBootmgrCommandOptions, + ffParseBootmgrJsonObject, + ffPrintBootmgr, + ffGenerateBootmgrJsonResult, + ffPrintBootmgrHelpFormat, + ffGenerateBootmgrJsonConfig + ); + ffOptionInitModuleArg(&options->moduleArgs); +} + +void ffDestroyBootmgrOptions(FFBootmgrOptions* options) +{ + ffOptionDestroyModuleArg(&options->moduleArgs); +} diff --git a/src/modules/bootmgr/bootmgr.h b/src/modules/bootmgr/bootmgr.h new file mode 100644 index 000000000..31a3b7946 --- /dev/null +++ b/src/modules/bootmgr/bootmgr.h @@ -0,0 +1,9 @@ +#pragma once + +#include "fastfetch.h" + +#define FF_BOOTMGR_MODULE_NAME "Bootmgr" + +void ffPrintBootmgr(FFBootmgrOptions* options); +void ffInitBootmgrOptions(FFBootmgrOptions* options); +void ffDestroyBootmgrOptions(FFBootmgrOptions* options); diff --git a/src/modules/bootmgr/option.h b/src/modules/bootmgr/option.h new file mode 100644 index 000000000..04a7d3cc5 --- /dev/null +++ b/src/modules/bootmgr/option.h @@ -0,0 +1,11 @@ +#pragma once + +// This file will be included in "fastfetch.h", do NOT put unnecessary things here + +#include "common/option.h" + +typedef struct FFBootmgrOptions +{ + FFModuleBaseInfo moduleInfo; + FFModuleArgs moduleArgs; +} FFBootmgrOptions; diff --git a/src/modules/cpu/cpu.c b/src/modules/cpu/cpu.c index 43ec9fe9a..5ec868c8c 100644 --- a/src/modules/cpu/cpu.c +++ b/src/modules/cpu/cpu.c @@ -6,16 +6,24 @@ #include "modules/cpu/cpu.h" #include "util/stringUtils.h" -#define FF_CPU_NUM_FORMAT_ARGS 8 +#define FF_CPU_NUM_FORMAT_ARGS 10 + +static int sortCores(const FFCPUCore* a, const FFCPUCore* b) +{ + return (int)b->freq - (int)a->freq; +} void ffPrintCPU(FFCPUOptions* options) { - FFCPUResult cpu; - cpu.temperature = FF_CPU_TEMP_UNSET; - cpu.coresPhysical = cpu.coresLogical = cpu.coresOnline = 0; - cpu.frequencyMin = cpu.frequencyMax = cpu.frequencyBase = 0.0/0.0; - ffStrbufInit(&cpu.name); - ffStrbufInit(&cpu.vendor); + FFCPUResult cpu = { + .temperature = FF_CPU_TEMP_UNSET, + .frequencyMin = 0.0/0.0, + .frequencyMax = 0.0/0.0, + .frequencyBase = 0.0/0.0, + .frequencyBiosLimit = 0.0/0.0, + .name = ffStrbufCreate(), + .vendor = ffStrbufCreate() + }; const char* error = ffDetectCPU(options, &cpu); @@ -48,10 +56,12 @@ void ffPrintCPU(FFCPUOptions* options) if(cpu.coresOnline > 1) ffStrbufAppendF(&str, " (%u)", cpu.coresOnline); - double freq = cpu.frequencyMax; - if(freq != freq) + double freq = cpu.frequencyBiosLimit; + if(freq <= 0.0000001) + freq = cpu.frequencyMax; + if(freq <= 0.0000001) freq = cpu.frequencyBase; - if(freq == freq) + if(freq > 0.0000001) ffStrbufAppendF(&str, " @ %.*f GHz", options->freqNdigits, freq); if(cpu.temperature == cpu.temperature) //FF_CPU_TEMP_UNSET @@ -64,6 +74,33 @@ void ffPrintCPU(FFCPUOptions* options) } else { + FF_STRBUF_AUTO_DESTROY coreTypes = ffStrbufCreate(); + uint32_t typeCount = 0; + while (cpu.coreTypes[typeCount].count != 0 && typeCount < sizeof(cpu.coreTypes) / sizeof(cpu.coreTypes[0])) typeCount++; + if (typeCount > 0) + { + qsort(cpu.coreTypes, typeCount, sizeof(cpu.coreTypes[0]), (void*) sortCores); + + for (uint32_t i = 0; i < typeCount; i++) + ffStrbufAppendF(&coreTypes, "%s%u", i == 0 ? "" : "+", cpu.coreTypes[i].count); + } + else + ffStrbufAppendF(&coreTypes, "%u", cpu.coresOnline); + + char freqBase[32], freqMax[32], freqBioslimit[32]; + if (cpu.frequencyBase > 0) + snprintf(freqBase, sizeof(freqBase), "%.*f", options->freqNdigits, cpu.frequencyBase); + else + freqBase[0] = 0; + if (cpu.frequencyMax > 0) + snprintf(freqMax, sizeof(freqMax), "%.*f", options->freqNdigits, cpu.frequencyMax); + else + freqMax[0] = 0; + if (cpu.frequencyBiosLimit > 0) + snprintf(freqBioslimit, sizeof(freqBioslimit), "%.*f", options->freqNdigits, cpu.frequencyBiosLimit); + else + freqBioslimit[0] = 0; + FF_STRBUF_AUTO_DESTROY tempStr = ffStrbufCreate(); ffTempsAppendNum(cpu.temperature, &tempStr, options->tempConfig, &options->moduleArgs); FF_PRINT_FORMAT_CHECKED(FF_CPU_MODULE_NAME, 0, &options->moduleArgs, FF_PRINT_TYPE_DEFAULT, FF_CPU_NUM_FORMAT_ARGS, ((FFformatarg[]){ @@ -72,9 +109,11 @@ void ffPrintCPU(FFCPUOptions* options) {FF_FORMAT_ARG_TYPE_UINT16, &cpu.coresPhysical}, {FF_FORMAT_ARG_TYPE_UINT16, &cpu.coresLogical}, {FF_FORMAT_ARG_TYPE_UINT16, &cpu.coresOnline}, - {FF_FORMAT_ARG_TYPE_DOUBLE, &cpu.frequencyBase}, - {FF_FORMAT_ARG_TYPE_DOUBLE, &cpu.frequencyMax}, - {FF_FORMAT_ARG_TYPE_STRBUF, &tempStr} + {FF_FORMAT_ARG_TYPE_STRING, freqBase}, + {FF_FORMAT_ARG_TYPE_STRING, freqMax}, + {FF_FORMAT_ARG_TYPE_STRBUF, &tempStr}, + {FF_FORMAT_ARG_TYPE_STRBUF, &coreTypes}, + {FF_FORMAT_ARG_TYPE_STRING, freqBioslimit}, })); } } @@ -143,12 +182,15 @@ void ffGenerateCPUJsonConfig(FFCPUOptions* options, yyjson_mut_doc* doc, yyjson_ void ffGenerateCPUJsonResult(FFCPUOptions* options, yyjson_mut_doc* doc, yyjson_mut_val* module) { - FFCPUResult cpu; - cpu.temperature = FF_CPU_TEMP_UNSET; - cpu.coresPhysical = cpu.coresLogical = cpu.coresOnline = 0; - cpu.frequencyMin = cpu.frequencyMax = cpu.frequencyBase = 0.0/0.0; - ffStrbufInit(&cpu.name); - ffStrbufInit(&cpu.vendor); + FFCPUResult cpu = { + .temperature = FF_CPU_TEMP_UNSET, + .frequencyMin = 0.0/0.0, + .frequencyMax = 0.0/0.0, + .frequencyBase = 0.0/0.0, + .frequencyBiosLimit = 0.0/0.0, + .name = ffStrbufCreate(), + .vendor = ffStrbufCreate() + }; const char* error = ffDetectCPU(options, &cpu); @@ -175,6 +217,15 @@ void ffGenerateCPUJsonResult(FFCPUOptions* options, yyjson_mut_doc* doc, yyjson_ yyjson_mut_obj_add_real(doc, frequency, "base", cpu.frequencyBase); yyjson_mut_obj_add_real(doc, frequency, "max", cpu.frequencyMax); yyjson_mut_obj_add_real(doc, frequency, "min", cpu.frequencyMin); + yyjson_mut_obj_add_real(doc, frequency, "biosLimit", cpu.frequencyBiosLimit); + + yyjson_mut_val* coreTypes = yyjson_mut_obj_add_arr(doc, obj, "coreTypes"); + for (uint32_t i = 0; i < sizeof (cpu.coreTypes) / sizeof (cpu.coreTypes[0]) && cpu.coreTypes[i].count > 0; i++) + { + yyjson_mut_val* core = yyjson_mut_arr_add_obj(doc, coreTypes); + yyjson_mut_obj_add_uint(doc, core, "count", cpu.coreTypes[i].count); + yyjson_mut_obj_add_uint(doc, core, "freq", cpu.coreTypes[i].freq); + } yyjson_mut_obj_add_real(doc, obj, "temperature", cpu.temperature); } @@ -193,7 +244,9 @@ void ffPrintCPUHelpFormat(void) "Online core count", "Base frequency", "Max frequency", - "Temperature (formatted)" + "Temperature (formatted)", + "Logical core count grouped by frequency", + "Bios limited frequency", })); } diff --git a/src/modules/gpu/gpu.c b/src/modules/gpu/gpu.c index 47d9fa91e..b687b5a46 100644 --- a/src/modules/gpu/gpu.c +++ b/src/modules/gpu/gpu.c @@ -127,6 +127,7 @@ void ffPrintGPU(FFGPUOptions* options) ffStrbufDestroy(&gpu->vendor); ffStrbufDestroy(&gpu->name); ffStrbufDestroy(&gpu->driver); + ffStrbufDestroy(&gpu->platformApi); } } @@ -143,9 +144,15 @@ bool ffParseGPUCommandOptions(FFGPUOptions* options, const char* key, const char return true; } - if (ffStrEqualsIgnCase(subKey, "force-vulkan")) + if (ffStrEqualsIgnCase(subKey, "detection-method")) { - options->forceVulkan = ffOptionParseBoolean(value); + options->detectionMethod = (FFGPUDetectionMethod) ffOptionParseEnum(key, value, (FFKeyValuePair[]) { + { "auto", FF_GPU_DETECTION_METHOD_AUTO }, + { "pci", FF_GPU_DETECTION_METHOD_PCI }, + { "vulkan", FF_GPU_DETECTION_METHOD_VULKAN }, + { "opengl", FF_GPU_DETECTION_METHOD_OPENGL }, + {}, + }); return true; } @@ -190,9 +197,20 @@ void ffParseGPUJsonObject(FFGPUOptions* options, yyjson_val* module) continue; } - if (ffStrEqualsIgnCase(key, "forceVulkan")) + if (ffStrEqualsIgnCase(key, "detectionMethod")) { - options->forceVulkan = yyjson_get_bool(val); + int value; + const char* error = ffJsonConfigParseEnum(val, &value, (FFKeyValuePair[]) { + { "auto", FF_GPU_DETECTION_METHOD_AUTO }, + { "pci", FF_GPU_DETECTION_METHOD_PCI }, + { "vulkan", FF_GPU_DETECTION_METHOD_VULKAN }, + { "opengl", FF_GPU_DETECTION_METHOD_OPENGL }, + {}, + }); + if (error) + ffPrintError(FF_GPU_MODULE_NAME, 0, &options->moduleArgs, FF_PRINT_TYPE_DEFAULT, "Invalid %s value: %s", key, error); + else + options->detectionMethod = (FFGPUDetectionMethod) value; continue; } @@ -229,8 +247,24 @@ void ffGenerateGPUJsonConfig(FFGPUOptions* options, yyjson_mut_doc* doc, yyjson_ if (options->driverSpecific != defaultOptions.driverSpecific) yyjson_mut_obj_add_bool(doc, module, "driverSpecific", options->driverSpecific); - if (options->forceVulkan != defaultOptions.forceVulkan) - yyjson_mut_obj_add_bool(doc, module, "forceVulkan", options->forceVulkan); + if (options->detectionMethod != defaultOptions.detectionMethod) + { + switch (options->detectionMethod) + { + case FF_GPU_DETECTION_METHOD_AUTO: + yyjson_mut_obj_add_str(doc, module, "detectionMethod", "auto"); + break; + case FF_GPU_DETECTION_METHOD_PCI: + yyjson_mut_obj_add_str(doc, module, "detectionMethod", "pci"); + break; + case FF_GPU_DETECTION_METHOD_VULKAN: + yyjson_mut_obj_add_str(doc, module, "detectionMethod", "vulkan"); + break; + case FF_GPU_DETECTION_METHOD_OPENGL: + yyjson_mut_obj_add_str(doc, module, "detectionMethod", "opengl"); + break; + } + } ffTempsGenerateJsonConfig(doc, module, defaultOptions.temp, defaultOptions.tempConfig, options->temp, options->tempConfig); @@ -327,6 +361,7 @@ void ffGenerateGPUJsonResult(FFGPUOptions* options, yyjson_mut_doc* doc, yyjson_ ffStrbufDestroy(&gpu->vendor); ffStrbufDestroy(&gpu->name); ffStrbufDestroy(&gpu->driver); + ffStrbufDestroy(&gpu->platformApi); } } @@ -364,7 +399,7 @@ void ffInitGPUOptions(FFGPUOptions* options) ffOptionInitModuleArg(&options->moduleArgs); options->driverSpecific = false; - options->forceVulkan = false; + options->detectionMethod = FF_GPU_DETECTION_METHOD_AUTO; options->temp = false; options->hideType = FF_GPU_TYPE_UNKNOWN; options->tempConfig = (FFColorRangeConfig) { 60, 80 }; diff --git a/src/modules/gpu/option.h b/src/modules/gpu/option.h index a68b8ad3a..405b7b11e 100644 --- a/src/modules/gpu/option.h +++ b/src/modules/gpu/option.h @@ -12,15 +12,24 @@ typedef enum FFGPUType FF_GPU_TYPE_DISCRETE, } FFGPUType; +typedef enum FFGPUDetectionMethod +{ + FF_GPU_DETECTION_METHOD_AUTO, + FF_GPU_DETECTION_METHOD_PCI, + FF_GPU_DETECTION_METHOD_VULKAN, + FF_GPU_DETECTION_METHOD_OPENGL, +} FFGPUDetectionMethod; + typedef struct FFGPUOptions { FFModuleBaseInfo moduleInfo; FFModuleArgs moduleArgs; FFGPUType hideType; + FFGPUDetectionMethod detectionMethod; bool temp; bool driverSpecific; - bool forceVulkan; + bool forceMethod; FFColorRangeConfig tempConfig; FFColorRangeConfig percent; } FFGPUOptions; diff --git a/src/modules/modules.h b/src/modules/modules.h index 7bb11f8b2..50c2a4e96 100644 --- a/src/modules/modules.h +++ b/src/modules/modules.h @@ -7,6 +7,7 @@ #include "modules/bluetooth/bluetooth.h" #include "modules/brightness/brightness.h" #include "modules/board/board.h" +#include "modules/bootmgr/bootmgr.h" #include "modules/break/break.h" #include "modules/camera/camera.h" #include "modules/chassis/chassis.h" diff --git a/src/modules/options.h b/src/modules/options.h index cfcf931d0..3972eff1b 100644 --- a/src/modules/options.h +++ b/src/modules/options.h @@ -6,6 +6,7 @@ #include "modules/bios/option.h" #include "modules/bluetooth/option.h" #include "modules/board/option.h" +#include "modules/bootmgr/option.h" #include "modules/break/option.h" #include "modules/brightness/option.h" #include "modules/camera/option.h" diff --git a/src/modules/packages/option.h b/src/modules/packages/option.h index 66d332d5d..f294b916c 100644 --- a/src/modules/packages/option.h +++ b/src/modules/packages/option.h @@ -28,6 +28,8 @@ typedef enum FFPackagesFlags FF_PACKAGES_FLAG_XBPS_BIT = 1 << 18, FF_PACKAGES_FLAG_AM_BIT = 1 << 19, FF_PACKAGES_FLAG_SORCERY_BIT = 1 << 20, + FF_PACKAGES_FLAG_LPKG_BIT = 1 << 21, + FF_PACKAGES_FLAG_LPKGBUILD_BIT = 1 << 22, } FFPackagesFlags; typedef struct FFPackagesOptions diff --git a/src/modules/packages/packages.c b/src/modules/packages/packages.c index 52ab6906d..229e1f43c 100644 --- a/src/modules/packages/packages.c +++ b/src/modules/packages/packages.c @@ -4,7 +4,7 @@ #include "modules/packages/packages.h" #include "util/stringUtils.h" -#define FF_PACKAGES_NUM_FORMAT_ARGS 30 +#define FF_PACKAGES_NUM_FORMAT_ARGS 32 void ffPrintPackages(FFPackagesOptions* options) { @@ -66,6 +66,8 @@ void ffPrintPackages(FFPackagesOptions* options) FF_PRINT_PACKAGE(opkg) FF_PRINT_PACKAGE(am) FF_PRINT_PACKAGE(sorcery) + FF_PRINT_PACKAGE(lpkg) + FF_PRINT_PACKAGE(lpkgbuild) putchar('\n'); } @@ -102,6 +104,8 @@ void ffPrintPackages(FFPackagesOptions* options) {FF_FORMAT_ARG_TYPE_UINT, &counts.opkg}, {FF_FORMAT_ARG_TYPE_UINT, &counts.am}, {FF_FORMAT_ARG_TYPE_UINT, &counts.sorcery}, + {FF_FORMAT_ARG_TYPE_UINT, &counts.lpkg}, + {FF_FORMAT_ARG_TYPE_UINT, &counts.lpkgbuild}, {FF_FORMAT_ARG_TYPE_UINT, &nixAll}, {FF_FORMAT_ARG_TYPE_UINT, &flatpakAll}, {FF_FORMAT_ARG_TYPE_UINT, &brewAll}, @@ -153,6 +157,10 @@ bool ffParsePackagesCommandOptions(FFPackagesOptions* options, const char* key, case 'F': if (false); FF_TEST_PACKAGE_NAME(FLATPAK) break; + case 'L': if (false); + FF_TEST_PACKAGE_NAME(LPKG) + FF_TEST_PACKAGE_NAME(LPKGBUILD) + break; case 'M': if (false); FF_TEST_PACKAGE_NAME(MACPORTS) break; @@ -254,6 +262,10 @@ void ffParsePackagesJsonObject(FFPackagesOptions* options, yyjson_val* module) case 'F': if (false); FF_TEST_PACKAGE_NAME(FLATPAK) break; + case 'L': if (false); + FF_TEST_PACKAGE_NAME(LPKG) + FF_TEST_PACKAGE_NAME(LPKGBUILD) + break; case 'M': if (false); FF_TEST_PACKAGE_NAME(MACPORTS) break; @@ -313,6 +325,8 @@ void ffGeneratePackagesJsonConfig(FFPackagesOptions* options, yyjson_mut_doc* do FF_TEST_PACKAGE_NAME(EMERGE) FF_TEST_PACKAGE_NAME(EOPKG) FF_TEST_PACKAGE_NAME(FLATPAK) + FF_TEST_PACKAGE_NAME(LPKG) + FF_TEST_PACKAGE_NAME(LPKGBUILD) FF_TEST_PACKAGE_NAME(NIX) FF_TEST_PACKAGE_NAME(OPKG) FF_TEST_PACKAGE_NAME(PACMAN) @@ -379,7 +393,7 @@ void ffGeneratePackagesJsonResult(FF_MAYBE_UNUSED FFPackagesOptions* options, yy void ffPrintPackagesHelpFormat(void) { - FF_PRINT_MODULE_FORMAT_HELP_CHECKED(FF_PACKAGES_MODULE_NAME, "{2} (pacman){?3}[{3}]{?}, {4} (dpkg), {5} (rpm), {6} (emerge), {7} (eopkg), {8} (xbps), {9} (nix-system), {10} (nix-user), {11} (nix-default), {12} (apk), {13} (pkg), {14} (flatpak-system), {15} (flatpack-user), {16} (snap), {17} (brew), {18} (brew-cask), {19} (MacPorts), {20} (scoop), {21} (choco), {22} (pkgtool), {23} (paludis), {24} (winget), {25} (opkg), {26} (am), {27} (sorcery)", FF_PACKAGES_NUM_FORMAT_ARGS, ((const char* []) { + FF_PRINT_MODULE_FORMAT_HELP_CHECKED(FF_PACKAGES_MODULE_NAME, "{2} (pacman){?3}[{3}]{?}, {4} (dpkg), {5} (rpm), {6} (emerge), {7} (eopkg), {8} (xbps), {9} (nix-system), {10} (nix-user), {11} (nix-default), {12} (apk), {13} (pkg), {14} (flatpak-system), {15} (flatpack-user), {16} (snap), {17} (brew), {18} (brew-cask), {19} (MacPorts), {20} (scoop), {21} (choco), {22} (pkgtool), {23} (paludis), {24} (winget), {25} (opkg), {26} (am), {27} (sorcery), {28} (lpkg), {29} (lpkgbuild)", FF_PACKAGES_NUM_FORMAT_ARGS, ((const char* []) { "Number of all packages", "Number of pacman packages", "Pacman branch on manjaro", @@ -407,6 +421,8 @@ void ffPrintPackagesHelpFormat(void) "Number of opkg packages", "Number of am packages", "Number of sorcery packages", + "Number of lpkg packages", + "Number of lpkgbuild packages", "Total number of all nix packages", "Total number of all flatpak packages", "Total number of all brew packages", diff --git a/src/options/modules.c b/src/options/modules.c index d3a501c32..77653becd 100644 --- a/src/options/modules.c +++ b/src/options/modules.c @@ -7,6 +7,7 @@ void ffOptionsInitModules(FFOptionsModules* options) ffInitBiosOptions(&options->bios); ffInitBluetoothOptions(&options->bluetooth); ffInitBoardOptions(&options->board); + ffInitBootmgrOptions(&options->bootmgr); ffInitBreakOptions(&options->break_); ffInitBrightnessOptions(&options->brightness); ffInitCameraOptions(&options->camera); @@ -73,6 +74,7 @@ void ffOptionsDestroyModules(FFOptionsModules* options) ffDestroyBiosOptions(&options->bios); ffDestroyBluetoothOptions(&options->bluetooth); ffDestroyBoardOptions(&options->board); + ffDestroyBootmgrOptions(&options->bootmgr); ffDestroyBreakOptions(&options->break_); ffDestroyBrightnessOptions(&options->brightness); ffDestroyCameraOptions(&options->camera); diff --git a/src/options/modules.h b/src/options/modules.h index dcd6e5bdc..3d5597e31 100644 --- a/src/options/modules.h +++ b/src/options/modules.h @@ -8,6 +8,7 @@ typedef struct FFOptionsModules FFBiosOptions bios; FFBluetoothOptions bluetooth; FFBoardOptions board; + FFBootmgrOptions bootmgr; FFBreakOptions break_; FFBrightnessOptions brightness; FFCPUOptions cpu; diff --git a/src/util/platform/FFPlatform_windows.c b/src/util/platform/FFPlatform_windows.c index 0ad0a62c8..d5c690c63 100644 --- a/src/util/platform/FFPlatform_windows.c +++ b/src/util/platform/FFPlatform_windows.c @@ -3,15 +3,11 @@ #include "util/stringUtils.h" #include "util/windows/unicode.h" #include "util/windows/registry.h" +#include "util/windows/nt.h" -#include #include #include -NTSTATUS NTAPI RtlGetVersion( - _Inout_ PRTL_OSVERSIONINFOW lpVersionInformation -); - static void getExePath(FFPlatform* platform) { wchar_t exePathW[MAX_PATH]; @@ -184,8 +180,13 @@ static void getSystemReleaseAndVersion(FFPlatform* platform) (unsigned) ubr); ffStrbufInit(&platform->systemDisplayVersion); - if(!ffRegReadStrbuf(hKey, L"DisplayVersion", &platform->systemDisplayVersion, NULL) && osVersion.szCSDVersion[0]) - ffStrbufSetWS(&platform->systemDisplayVersion, osVersion.szCSDVersion); + if(!ffRegReadStrbuf(hKey, L"DisplayVersion", &platform->systemDisplayVersion, NULL)) + { + if (osVersion.szCSDVersion[0]) + ffStrbufSetWS(&platform->systemDisplayVersion, osVersion.szCSDVersion); + else + ffRegReadStrbuf(hKey, L"ReleaseId", &platform->systemDisplayVersion, NULL); // For old Windows 10 + } ffRegReadStrbuf(hKey, L"BuildLabEx", &platform->systemVersion, NULL); diff --git a/src/util/smbiosHelper.c b/src/util/smbiosHelper.c index 13b119ec4..39d3e94a1 100644 --- a/src/util/smbiosHelper.c +++ b/src/util/smbiosHelper.c @@ -79,6 +79,26 @@ bool ffGetSmbiosValue(const char* devicesPath, const char* classPath, FFstrbuf* return false; } +typedef struct FFSmbios20EntryPoint +{ + uint8_t AnchorString[4]; + uint8_t EntryPointStructureChecksum; + uint8_t EntryPointLength; + uint8_t SmbiosMajorVersion; + uint8_t SmbiosMinorVersion; + uint16_t MaximumStructureSize; + uint8_t EntryPointRevision; + uint8_t FormattedArea[5]; + uint8_t IntermediateAnchorString[5]; + uint8_t IntermediateChecksum; + uint16_t StructureTableLength; + uint32_t StructureTableAddress; + uint16_t NumberOfSmbiosStructures; + uint8_t SmbiosBcdRevision; +} __attribute__((__packed__)) FFSmbios20EntryPoint; +static_assert(offsetof(FFSmbios20EntryPoint, SmbiosBcdRevision) == 0x1E, + "FFSmbios30EntryPoint: Wrong struct alignment"); + typedef struct FFSmbios30EntryPoint { uint8_t AnchorString[5]; @@ -94,7 +114,13 @@ typedef struct FFSmbios30EntryPoint } __attribute__((__packed__)) FFSmbios30EntryPoint; static_assert(offsetof(FFSmbios30EntryPoint, StructureTableAddress) == 0x10, - "FFSmbiosProcessorInfo: Wrong struct alignment"); + "FFSmbios30EntryPoint: Wrong struct alignment"); + +typedef union FFSmbiosEntryPoint +{ + FFSmbios20EntryPoint Smbios20; + FFSmbios30EntryPoint Smbios30; +} FFSmbiosEntryPoint; const FFSmbiosHeaderTable* ffGetSmbiosHeaderTable() { @@ -107,55 +133,73 @@ const FFSmbiosHeaderTable* ffGetSmbiosHeaderTable() if (!ffAppendFileBuffer("/sys/firmware/dmi/tables/DMI", &buffer)) #endif { - FF_STRBUF_AUTO_DESTROY strEntry = ffStrbufCreate(); - // Only support SMBIOS 3.x for simplification + FF_STRBUF_AUTO_DESTROY strEntryAddress = ffStrbufCreate(); #ifdef __FreeBSD__ - if (!ffSettingsGetFreeBSDKenv("hint.smbios.0.mem", &strEntry)) + if (!ffSettingsGetFreeBSDKenv("hint.smbios.0.mem", &strEntryAddress)) return NULL; #else - if (!ffParsePropFile("/sys/firmware/efi/systab", "SMBIOS3=", &strEntry)) - return NULL; + { + FF_STRBUF_AUTO_DESTROY systab = ffStrbufCreate(); + if (!ffAppendFileBuffer("/sys/firmware/efi/systab", &systab)) + return NULL; + if (!ffParsePropLines(systab.chars, "SMBIOS3=", &strEntryAddress) && + !ffParsePropLines(systab.chars, "SMBIOS=", &strEntryAddress)) + return NULL; + } #endif - loff_t pEntry = (loff_t) strtol(strEntry.chars, NULL, 0); - if (pEntry == 0) return NULL; + loff_t entryAddress = (loff_t) strtol(strEntryAddress.chars, NULL, 16); + if (entryAddress == 0) return NULL; FF_AUTO_CLOSE_FD int fd = open("/dev/mem", O_RDONLY); if (fd < 0) return NULL; - FFSmbios30EntryPoint entryPoint; - if (pread(fd, &entryPoint, sizeof(entryPoint), pEntry) != sizeof(entryPoint)) + FFSmbiosEntryPoint entryPoint; + if (pread(fd, &entryPoint, sizeof(entryPoint), entryAddress) < 0x10) { // `pread /dev/mem` returns EFAULT in FreeBSD // https://stackoverflow.com/questions/69372330/how-to-read-dev-mem-using-read - void* p = mmap(NULL, sizeof(entryPoint), PROT_READ, MAP_SHARED, fd, pEntry); + void* p = mmap(NULL, sizeof(entryPoint), PROT_READ, MAP_SHARED, fd, entryAddress); if (p == MAP_FAILED) return NULL; memcpy(&entryPoint, p, sizeof(entryPoint)); munmap(p, sizeof(entryPoint)); } - if (memcmp(entryPoint.AnchorString, "_SM3_", sizeof(entryPoint.AnchorString)) != 0 || - entryPoint.EntryPointLength != sizeof(entryPoint)) - return NULL; + uint32_t tableLength = 0; + loff_t tableAddress = 0; + if (memcmp(entryPoint.Smbios20.AnchorString, "_SM_", sizeof(entryPoint.Smbios20.AnchorString)) == 0) + { + if (entryPoint.Smbios20.EntryPointLength != sizeof(entryPoint.Smbios20)) + return NULL; + tableLength = entryPoint.Smbios20.StructureTableLength; + tableAddress = (loff_t) entryPoint.Smbios20.StructureTableAddress; + } + else if (memcmp(entryPoint.Smbios30.AnchorString, "_SM3_", sizeof(entryPoint.Smbios30.AnchorString)) == 0) + { + if (entryPoint.Smbios30.EntryPointLength != sizeof(entryPoint.Smbios30)) + return NULL; + tableLength = entryPoint.Smbios30.StructureTableMaximumSize; + tableAddress = (loff_t) entryPoint.Smbios30.StructureTableAddress; + } - ffStrbufEnsureFixedLengthFree(&buffer, entryPoint.StructureTableMaximumSize); - if (pread(fd, buffer.chars, entryPoint.StructureTableMaximumSize, pEntry) == (loff_t) entryPoint.StructureTableMaximumSize) + ffStrbufEnsureFixedLengthFree(&buffer, tableLength); + if (pread(fd, buffer.chars, tableLength, tableAddress) == tableLength) { - buffer.length = (uint32_t) entryPoint.StructureTableMaximumSize; + buffer.length = tableLength; buffer.chars[buffer.length] = '\0'; } else { // entryPoint.StructureTableAddress must be page aligned. // Unaligned physical memory access results in all kinds of crashes. - void* p = mmap(NULL, entryPoint.StructureTableMaximumSize, PROT_READ, MAP_SHARED, fd, (loff_t) entryPoint.StructureTableAddress); + void* p = mmap(NULL, tableLength, PROT_READ, MAP_SHARED, fd, tableAddress); if (p == MAP_FAILED) { ffStrbufDestroy(&buffer); // free buffer and reset state return NULL; } - ffStrbufSetNS(&buffer, entryPoint.StructureTableMaximumSize, (char*) p); - munmap(p, entryPoint.StructureTableMaximumSize); + ffStrbufSetNS(&buffer, tableLength, (char*) p); + munmap(p, tableLength); } } diff --git a/src/util/windows/nt.h b/src/util/windows/nt.h new file mode 100644 index 000000000..53b33169b --- /dev/null +++ b/src/util/windows/nt.h @@ -0,0 +1,25 @@ +#pragma once + +#include +#include + +typedef struct _PROCESSOR_POWER_INFORMATION { + ULONG Number; + ULONG MaxMhz; + ULONG CurrentMhz; + ULONG MhzLimit; + ULONG MaxIdleState; + ULONG CurrentIdleState; +} PROCESSOR_POWER_INFORMATION, *PPROCESSOR_POWER_INFORMATION; + +NTSTATUS NTAPI NtPowerInformation( + IN POWER_INFORMATION_LEVEL InformationLevel, + IN PVOID InputBuffer OPTIONAL, + IN ULONG InputBufferLength, + OUT PVOID OutputBuffer OPTIONAL, + IN ULONG OutputBufferLength); + + +NTSTATUS NTAPI RtlGetVersion( + _Inout_ PRTL_OSVERSIONINFOW lpVersionInformation +);