diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index c5e0545916..83e824db63 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -683,7 +683,6 @@ jobs: haiku-amd64: name: Haiku-amd64 runs-on: ubuntu-latest - if: false permissions: security-events: write contents: read @@ -691,17 +690,45 @@ jobs: - name: checkout repository uses: actions/checkout@v4 - - name: run VM + - name: Fixup VM uses: cross-platform-actions/action@master with: operating_system: haiku + version: 'r1beta5' architecture: x86-64 cpu_count: 4 shell: bash + sync_files: false + shutdown_vm: false + run: | + mv '/home/runner/work' '/boot/home/work' + ln -sf '/boot/home/work' '/home/runner/work' + + - name: Sync files to VM + uses: cross-platform-actions/action@master + with: + operating_system: haiku version: 'r1beta5' + architecture: x86-64 + cpu_count: 4 + shell: bash + sync_files: runner-to-vm + shutdown_vm: false run: | - uname -a + chown -R $(id -u):$(id -g) $(pwd) ls -l + + - name: run VM + uses: cross-platform-actions/action@master + with: + operating_system: haiku + version: 'r1beta5' + architecture: x86-64 + cpu_count: 4 + shell: bash + sync_files: vm-to-runner + run: | + uname -a pkgman install -y git dbus_devel mesa_devel libelf_devel imagemagick_devel opencl_headers ocl_icd_devel vulkan_devel zlib_devel chafa_devel cmake gcc make pkgconfig python3.10 cmake -DSET_TWEAK=Off -DBUILD_TESTS=On -DENABLE_EMBEDDED_PCIIDS=On -DENABLE_EMBEDDED_AMDGPUIDS=On . cmake --build . --target package --verbose -j4 @@ -709,7 +736,6 @@ jobs: time ./fastfetch -c presets/ci.jsonc --stat false time ./fastfetch -c presets/ci.jsonc --format json time ./flashfetch - ldd fastfetch ctest --output-on-failure - name: upload artifacts @@ -861,6 +887,7 @@ jobs: - openbsd-amd64 - netbsd-amd64 - sunos-amd64 + - haiku-amd64 - windows-amd64 - windows-aarch64 permissions: diff --git a/CHANGELOG.md b/CHANGELOG.md index 4df7293738..34836c573d 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,3 +1,35 @@ +# 2.48.0 + +Features: +* Add support for detecting Fedora variants (#1830, OS, Linux) + * Currently supported variants: CoreOS, Kinoite, Sericea, Silverblue +* Optimize GPU detection on Windows when `--gpu-driver-specific` is not used (GPU, Windows) + * Improve accuracy of GPU type detection. Previously it was guessed based on the dedicated vmem size, which causes issues on newer AMD integrated GPUs such as 9000 HX and AI 9 HX series. Supported on Windows 8.1 or later. + * Add support for generic frequency detection of GPU 3D engine on Windows 11 22H2 or later. + * Improve performance. GPU temperature detection is significantly improved when `--gpu-driver-specific` is not used. +* Improve performance and security when spawning child processes by replacing `fork-exec` with `posix_spawn` (*nix) +* Improve accuracy of sound device detection on macOS (Sound, macOS) +* Trim leading and trailing whitespaces in disk serial numbers (PhysicalDisk) +* Add `/etc/profiles/per-user` detection for Nix user packages (#1782, Packages, Linux) +* Introduce `years` (whole years as integer), `days-of-year` (days since the last anniversary) and `years-fraction` (years as fraction, e.g. 1.5 means 1 year and 6 months) formatting placeholder to `Disk` (since disk creation), `Users` (since user login) and `Uptime` (since system boot) modules + * For example: `fastfetch -s disk --disk-key 'OS Installation' --disk-format '{years} years {days-of-year} days'` +* Add `--fraction-ndigits` option to specify the number of digits after the decimal point when displaying ordinary fractional values + * Typically used with `{years-fraction}` above + * This option does not affect percentage values, sizes, etc, which are controlled by individual options. + +Bugfixes: +* Fix compilation issues when not using `-DBINARY_LINK_TYPE=dlopen` + * Regression from v2.47.0 + * Note: this option was added for debugging purposes only and is not recommended for production use +* Replace `MTLDevice::hasUnifiedMemory` with `MTLDevice::location` for GPU type Detection (GPU, macOS) + * This should resolve the issue where discrete GPUs were detected as integrated GPUs on Intel MacBooks with multi-GPU configurations. +* Prevent text files from being loaded as image files (#1843, Logo) + +Logos: +* Add Minimal System +* Add AxOS +* Rename Ada to Xray OS + # 2.47.0 Features: diff --git a/CMakeLists.txt b/CMakeLists.txt index 967d532999..376942f235 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.47.0 + VERSION 2.48.0 LANGUAGES C DESCRIPTION "Fast neofetch-like system information tool" HOMEPAGE_URL "https://github.com/fastfetch-cli/fastfetch" @@ -336,7 +336,7 @@ endif() # Ascii image data # #################### -file(GLOB LOGO_FILES "src/logo/ascii/*.txt") +file(GLOB LOGO_FILES CONFIGURE_DEPENDS "src/logo/ascii/*.txt") set(LOGO_BUILTIN_H "#pragma once\n#pragma GCC diagnostic ignored \"-Wtrigraphs\"\n\n") foreach(file ${LOGO_FILES}) fastfetch_load_text("${file}" content) @@ -1582,7 +1582,6 @@ elseif(WIN32) PRIVATE "ws2_32" PRIVATE "ntdll" PRIVATE "version" - PRIVATE "setupapi" PRIVATE "hid" PRIVATE "wtsapi32" PRIVATE "imagehlp" diff --git a/README.md b/README.md index 29fa713629..f31b8b5116 100644 --- a/README.md +++ b/README.md @@ -10,6 +10,7 @@ [![GitHub release (with filter)](https://img.shields.io/github/v/release/fastfetch-cli/fastfetch?logo=github)](https://github.com/fastfetch-cli/fastfetch/releases) [![latest packaged version(s)](https://repology.org/badge/latest-versions/fastfetch.svg)](https://repology.org/project/fastfetch/versions) [![Packaging status](https://repology.org/badge/tiny-repos/fastfetch.svg)](https://repology.org/project/fastfetch/versions) +[![Ask DeepWiki](https://deepwiki.com/badge.svg)](https://deepwiki.com/fastfetch-cli/fastfetch) Fastfetch is a [neofetch](https://github.com/dylanaraps/neofetch)-like tool for fetching system information and displaying it in a visually appealing way. It is written mainly in C, with a focus on performance and customizability. Currently, it supports Linux, macOS, Windows 7+, Android, FreeBSD, OpenBSD, NetBSD, DragonFly, Haiku, and SunOS. @@ -92,7 +93,7 @@ See the Wiki: https://github.com/fastfetch-cli/fastfetch/wiki/Building * Run with default configuration: `fastfetch` * Run with [all supported modules](https://github.com/fastfetch-cli/fastfetch/wiki/Support+Status#available-modules) to find what interests you: `fastfetch -c all.jsonc` -* View all data that fastfetch detects: `fastfetch -s --format json` +* View all data that fastfetch detects: `fastfetch -s [:][:] --format json` * Display help messages: `fastfetch --help` * Generate a config file based on command line arguments: `fastfetch --arg1 --arg2 --gen-config` diff --git a/debian/changelog b/debian/changelog index cb8543611d..0072fed021 100644 --- a/debian/changelog +++ b/debian/changelog @@ -1,3 +1,9 @@ +fastfetch (2.47.0) jammy; urgency=medium + + * Update to 2.47.0 + + -- Carter Li Thu, 03 Jul 2025 14:51:45 +0800 + fastfetch (2.46.0) jammy; urgency=medium * Update to 2.46.0 diff --git a/debian/files b/debian/files index 7ba3c5e95f..c4e4b79089 100644 --- a/debian/files +++ b/debian/files @@ -1 +1 @@ -fastfetch_2.46.0_source.buildinfo universe/utils optional +fastfetch_2.47.0_source.buildinfo universe/utils optional diff --git a/doc/json_schema.json b/doc/json_schema.json index d2c4ff4173..75148a5deb 100644 --- a/doc/json_schema.json +++ b/doc/json_schema.json @@ -183,7 +183,7 @@ "type": "string" }, "diskFormat": { - "description": "Output format of the module `Disk`. See Wiki for formatting syntax\n 1. {size-used}: Size used\n 2. {size-total}: Size total\n 3. {size-percentage}: Size percentage num\n 4. {files-used}: Files used\n 5. {files-total}: Files total\n 6. {files-percentage}: Files percentage num\n 7. {is-external}: True if external volume\n 8. {is-hidden}: True if hidden volume\n 9. {filesystem}: Filesystem\n 10. {name}: Label / name\n 11. {is-readonly}: True if read-only\n 12. {create-time}: Create time in local timezone\n 13. {size-percentage-bar}: Size percentage bar\n 14. {files-percentage-bar}: Files percentage bar\n 15. {days}: Days after creation\n 16. {hours}: Hours after creation\n 17. {minutes}: Minutes after creation\n 18. {seconds}: Seconds after creation\n 19. {milliseconds}: Milliseconds after creation\n 20. {mountpoint}: Mount point / drive letter\n 21. {mount-from}: Mount from (device path)", + "description": "Output format of the module `Disk`. See Wiki for formatting syntax\n 1. {size-used}: Size used\n 2. {size-total}: Size total\n 3. {size-percentage}: Size percentage num\n 4. {files-used}: Files used\n 5. {files-total}: Files total\n 6. {files-percentage}: Files percentage num\n 7. {is-external}: True if external volume\n 8. {is-hidden}: True if hidden volume\n 9. {filesystem}: Filesystem\n 10. {name}: Label / name\n 11. {is-readonly}: True if read-only\n 12. {create-time}: Create time in local timezone\n 13. {size-percentage-bar}: Size percentage bar\n 14. {files-percentage-bar}: Files percentage bar\n 15. {days}: Days after creation\n 16. {hours}: Hours after creation\n 17. {minutes}: Minutes after creation\n 18. {seconds}: Seconds after creation\n 19. {milliseconds}: Milliseconds after creation\n 20. {mountpoint}: Mount point / drive letter\n 21. {mount-from}: Mount from (device path)\n 22. {years}: Years integer after creation\n 23. {days-of-year}: Days of year after creation\n 24. {years-fraction}: Years fraction after creation", "type": "string" }, "diskioFormat": { @@ -306,10 +306,6 @@ "description": "Output format of the module `PublicIp`. See Wiki for formatting syntax\n 1. {ip}: Public IP address\n 2. {location}: Location", "type": "string" }, - "separatorFormat": { - "description": "Output format of the module `Separator`. See Wiki for formatting syntax\n 1. {string}: Separator string\n 2. {outputColor}: Output color\n 3. {length}: Length", - "type": "string" - }, "shellFormat": { "description": "Output format of the module `Shell`. See Wiki for formatting syntax\n 1. {process-name}: Shell process name\n 2. {exe}: The first argument of the command line when running the shell\n 3. {exe-name}: Shell base name of arg0\n 4. {version}: Shell version\n 5. {pid}: Shell pid\n 6. {pretty-name}: Shell pretty name\n 7. {exe-path}: Shell full exe path\n 8. {tty}: Shell tty used", "type": "string" @@ -319,7 +315,7 @@ "type": "string" }, "swapFormat": { - "description": "Output format of the module `Swap`. See Wiki for formatting syntax\n 1. {used}: Used size\n 2. {total}: Total size\n 3. {percentage}: Percentage used (num)\n 4. {percentage-bar}: Percentage used (bar)", + "description": "Output format of the module `Swap`. See Wiki for formatting syntax\n 1. {used}: Used size\n 2. {total}: Total size\n 3. {percentage}: Percentage used (num)\n 4. {percentage-bar}: Percentage used (bar)\n 5. {name}: Name", "type": "string" }, "terminalFormat": { @@ -351,11 +347,11 @@ "type": "string" }, "uptimeFormat": { - "description": "Output format of the module `Uptime`. See Wiki for formatting syntax\n 1. {days}: Days\n 2. {hours}: Hours\n 3. {minutes}: Minutes\n 4. {seconds}: Seconds\n 5. {milliseconds}: Milliseconds\n 6. {boot-time}: Boot time in local timezone", + "description": "Output format of the module `Uptime`. See Wiki for formatting syntax\n 1. {days}: Days after boot\n 2. {hours}: Hours after boot\n 3. {minutes}: Minutes after boot\n 4. {seconds}: Seconds after boot\n 5. {milliseconds}: Milliseconds after boot\n 6. {boot-time}: Boot time in local timezone\n 7. {years}: Years integer after boot\n 8. {days-of-year}: Days of year after boot\n 9. {years-fraction}: Years fraction after boot", "type": "string" }, "usersFormat": { - "description": "Output format of the module `Users`. See Wiki for formatting syntax\n 1. {name}: User name\n 2. {host-name}: Host name\n 3. {session}: Session name\n 4. {client-ip}: Client IP\n 5. {login-time}: Login Time in local timezone\n 6. {days}: Days after login\n 7. {hours}: Hours after login\n 8. {minutes}: Minutes after login\n 9. {seconds}: Seconds after login\n 10. {milliseconds}: Milliseconds after login", + "description": "Output format of the module `Users`. See Wiki for formatting syntax\n 1. {name}: User name\n 2. {host-name}: Host name\n 3. {session}: Session name\n 4. {client-ip}: Client IP\n 5. {login-time}: Login Time in local timezone\n 6. {days}: Days after login\n 7. {hours}: Hours after login\n 8. {minutes}: Minutes after login\n 9. {seconds}: Seconds after login\n 10. {milliseconds}: Milliseconds after login\n 11. {years}: Years integer after login\n 12. {days-of-year}: Days of year after login\n 13. {years-fraction}: Years fraction after login", "type": "string" }, "versionFormat": { @@ -934,6 +930,28 @@ } } }, + "fraction": { + "type": "object", + "additionalProperties": false, + "description": "Set how ordinary fraction numbers should be displayed", + "properties": { + "ndigits": { + "oneOf": [ + { + "type": "number", + "description": "Set the number of digits to keep after the decimal point when formatting ordinary fraction numbers", + "minimum": 0, + "maximum": 9 + }, + { + "type": "null", + "description": "The number of digits will be automatically determined based on the value" + } + ], + "default": null + } + } + }, "noBuffer": { "type": "boolean", "description": "Whether to disable the stdout application buffer", diff --git a/presets/examples/27.jsonc b/presets/examples/27.jsonc new file mode 100644 index 0000000000..9fd3eadab8 --- /dev/null +++ b/presets/examples/27.jsonc @@ -0,0 +1,49 @@ +{ + "$schema": "https://github.com/fastfetch-cli/fastfetch/raw/dev/doc/json_schema.json", + "logo": { + "type": "small", + "padding": { + "top": 1 + } + }, + "display": { + "separator": " " + }, + "modules": [ + "break", + "title", + { + "type": "os", + "key": "os ", + "keyColor": "red" + }, + { + "type": "kernel", + "key": "kernel", + "keyColor": "green" + }, + { + "type": "host", + "format": "{vendor} {family}", + "key": "host ", + "keyColor": "yellow" + }, + { + "type": "packages", + "key": "pkgs ", + "keyColor": "blue" + }, + { + "type": "uptime", + "format": "{?days}{days}d {?}{hours}h {minutes}m", + "key": "uptime", + "keyColor": "magenta" + }, + { + "type": "memory", + "key": "memory", + "keyColor": "cyan" + }, + "break" + ] +} diff --git a/src/common/format.c b/src/common/format.c index 204c1d8d74..fa91992048 100644 --- a/src/common/format.c +++ b/src/common/format.c @@ -32,10 +32,16 @@ void ffFormatAppendFormatArg(FFstrbuf* buffer, const FFformatarg* formatarg) ffStrbufAppend(buffer, (FFstrbuf*)formatarg->value); break; case FF_FORMAT_ARG_TYPE_FLOAT: - ffStrbufAppendF(buffer, "%f", *(float*)formatarg->value); + if (instance.config.display.fractionNdigits >= 0) + ffStrbufAppendF(buffer, "%.*f", instance.config.display.fractionNdigits, *(float*)formatarg->value); + else + ffStrbufAppendF(buffer, "%g", *(float*)formatarg->value); break; case FF_FORMAT_ARG_TYPE_DOUBLE: - ffStrbufAppendF(buffer, "%g", *(double*)formatarg->value); + if (instance.config.display.fractionNdigits >= 0) + ffStrbufAppendF(buffer, "%.*f", instance.config.display.fractionNdigits, *(double*)formatarg->value); + else + ffStrbufAppendF(buffer, "%g", *(double*)formatarg->value); break; case FF_FORMAT_ARG_TYPE_BOOL: ffStrbufAppendS(buffer, *(bool*)formatarg->value ? "true" : "false"); @@ -108,6 +114,7 @@ static inline bool formatArgSet(const FFformatarg* arg) { return arg->value != NULL && ( (arg->type == FF_FORMAT_ARG_TYPE_DOUBLE && *(double*)arg->value > 0.0) || //Also is false for NaN + (arg->type == FF_FORMAT_ARG_TYPE_FLOAT && *(float*)arg->value > 0.0) || //Also is false for NaN (arg->type == FF_FORMAT_ARG_TYPE_INT && *(int*)arg->value > 0) || (arg->type == FF_FORMAT_ARG_TYPE_STRBUF && ((FFstrbuf*)arg->value)->length > 0) || (arg->type == FF_FORMAT_ARG_TYPE_STRING && ffStrSet((char*)arg->value)) || diff --git a/src/common/library.h b/src/common/library.h index 849b89ee37..b208e127ed 100644 --- a/src/common/library.h +++ b/src/common/library.h @@ -74,7 +74,7 @@ void* ffLibraryLoad(const char* path, int maxVersion, ...); FF_MAYBE_UNUSED void* libraryObjectName = NULL; // Placeholder #define FF_LIBRARY_LOAD_SYMBOL_ADDRESS(library, symbolMapping, symbolName, returnValue) \ - symbolMapping = (__typeof__(&symbolName)) &symbolName + symbolMapping = (__typeof__(&symbolName)) &symbolName; #define FF_LIBRARY_LOAD_SYMBOL(library, symbolName, returnValue) \ FF_MAYBE_UNUSED __typeof__(&symbolName) FF_LIBRARY_LOAD_SYMBOL_ADDRESS(library, ff ## symbolName, symbolName, returnValue); diff --git a/src/common/networking/networking_linux.c b/src/common/networking/networking_linux.c index c518044e47..80576d5871 100644 --- a/src/common/networking/networking_linux.c +++ b/src/common/networking/networking_linux.c @@ -7,7 +7,7 @@ #include "util/debug.h" #include -#include +#include #include #include #include diff --git a/src/common/processing_linux.c b/src/common/processing_linux.c index 770ed60b63..701ca48330 100644 --- a/src/common/processing_linux.c +++ b/src/common/processing_linux.c @@ -11,6 +11,7 @@ #include #include #include +#include #if defined(__FreeBSD__) || defined(__APPLE__) #include @@ -33,6 +34,10 @@ #include #endif +#ifndef environ +extern char** environ; +#endif + enum { FF_PIPE_BUFSIZ = 8192 }; static inline int ffPipe2(int* fds, int flags) @@ -48,6 +53,8 @@ static inline int ffPipe2(int* fds, int flags) #endif } + +// Not thread-safe const char* ffProcessAppendOutput(FFstrbuf* buffer, char* const argv[], bool useStdErr) { int pipes[2]; @@ -56,27 +63,62 @@ const char* ffProcessAppendOutput(FFstrbuf* buffer, char* const argv[], bool use if(ffPipe2(pipes, O_CLOEXEC) == -1) return "pipe() failed"; - pid_t childPid = fork(); - if(childPid == -1) + posix_spawn_file_actions_t file_actions; + posix_spawn_file_actions_init(&file_actions); + posix_spawn_file_actions_adddup2(&file_actions, pipes[1], useStdErr ? STDERR_FILENO : STDOUT_FILENO); + posix_spawn_file_actions_addopen(&file_actions, useStdErr ? STDOUT_FILENO : STDERR_FILENO, "/dev/null", O_WRONLY, 0); + pid_t childPid = -1; + + static char* oldLang = NULL; + static int langIndex = -1; + + if (langIndex >= 0) { - close(pipes[0]); - close(pipes[1]); - return "fork() failed"; + // Found before + if (oldLang) // oldLang was set only if it needed to be changed + { + if (environ[langIndex] != oldLang) + { + // environ is changed outside of this function + langIndex = -1; + } + else + environ[langIndex] = (char*) "LANG=C.UTF-8"; + } } - - //Child - if(childPid == 0) + if (langIndex < 0) { - int nullFile = open("/dev/null", O_WRONLY | O_CLOEXEC); - dup2(pipes[1], useStdErr ? STDERR_FILENO : STDOUT_FILENO); - dup2(nullFile, useStdErr ? STDOUT_FILENO : STDERR_FILENO); - setenv("LANG", "C", 1); - execvp(argv[0], argv); - _exit(127); + for (int i = 0; environ[i] != NULL; i++) + { + if (ffStrStartsWith(environ[i], "LANG=")) + { + langIndex = i; + const char* langValue = environ[i] + 5; // Skip "LANG=" + if (ffStrEqualsIgnCase(langValue, "C") || + ffStrStartsWithIgnCase(environ[i], "C.") || + ffStrEqualsIgnCase(langValue, "en_US") || + ffStrStartsWithIgnCase(langValue, "en_US.")) + break; // No need to change LANG + oldLang = environ[i]; + environ[i] = (char*) "LANG=C.UTF-8"; // Set LANG to C.UTF-8 for consistent output + break; + } + } } - //Parent + int ret = posix_spawnp(&childPid, argv[0], &file_actions, NULL, argv, environ); + + if (oldLang) + environ[langIndex] = oldLang; + + posix_spawn_file_actions_destroy(&file_actions); + close(pipes[1]); + if (ret != 0) + { + close(pipes[0]); + return "posix_spawnp() failed"; + } int FF_AUTO_CLOSE_FD childPipeFd = pipes[0]; char str[FF_PIPE_BUFSIZ]; @@ -86,20 +128,30 @@ const char* ffProcessAppendOutput(FFstrbuf* buffer, char* const argv[], bool use if (timeout >= 0) { struct pollfd pollfd = { childPipeFd, POLLIN, 0 }; - if (poll(&pollfd, 1, timeout) == 0) + int pollret = poll(&pollfd, 1, timeout); + if (pollret == 0) { kill(childPid, SIGTERM); waitpid(childPid, NULL, 0); return "poll(&pollfd, 1, timeout) timeout (try increasing --processing-timeout)"; } - else if (errno == EINTR) + else if (pollret < 0) { - // The child process has been terminated. See `chldSignalHandler` in `common/init.c` - if (waitpid(childPid, NULL, WNOHANG) == childPid) + if (errno == EINTR) + { + // The child process has been terminated. See `chldSignalHandler` in `common/init.c` + if (waitpid(childPid, NULL, WNOHANG) == childPid) + { + // Read remaining data from the pipe + fcntl(childPipeFd, F_SETFL, O_CLOEXEC | O_NONBLOCK); + childPid = -1; + } + } + else { - // Read remaining data from the pipe - fcntl(childPipeFd, F_SETFL, O_CLOEXEC | O_NONBLOCK); - childPid = -1; + kill(childPid, SIGTERM); + waitpid(childPid, NULL, 0); + return "poll(&pollfd, 1, timeout) error"; } } else if (pollfd.revents & POLLERR) diff --git a/src/common/time.h b/src/common/time.h index 0745574bbb..ed0fd11389 100644 --- a/src/common/time.h +++ b/src/common/time.h @@ -97,3 +97,47 @@ static inline const char* ffTimeToTimeStr(uint64_t msec) #ifdef _WIN32 #pragma GCC diagnostic pop #endif + +typedef struct FFTimeGetAgeResult +{ + uint32_t years; + uint32_t daysOfYear; + double yearsFraction; +} FFTimeGetAgeResult; + +static inline FFTimeGetAgeResult ffTimeGetAge(uint64_t birthMs, uint64_t nowMs) +{ + FFTimeGetAgeResult result = {}; + if (__builtin_expect(birthMs == 0 || nowMs < birthMs, 0)) + return result; + + time_t birth_s = (time_t) (birthMs / 1000); + struct tm birth_tm; + #ifdef _WIN32 + localtime_s(&birth_tm, &birth_s); + #else + localtime_r(&birth_s, &birth_tm); + #endif + + time_t now_s = (time_t) (nowMs / 1000); + struct tm now_tm; + #ifdef _WIN32 + localtime_s(&now_tm, &now_s); + #else + localtime_r(&now_s, &now_tm); + #endif + + result.years = (uint32_t) (now_tm.tm_year - birth_tm.tm_year); + if (now_tm.tm_yday < birth_tm.tm_yday) + result.years--; + + birth_tm.tm_year += result.years; + birth_s = mktime(&birth_tm); + uint32_t diff_s = (uint32_t) (now_s - birth_s); + result.daysOfYear = diff_s / (24 * 60 * 60); + + birth_tm.tm_year += 1; + result.yearsFraction = (double) diff_s / (double) (mktime(&birth_tm) - birth_s) + result.years; + + return result; +} diff --git a/src/data/help.json b/src/data/help.json index 4244fad56d..0c7302bbf4 100644 --- a/src/data/help.json +++ b/src/data/help.json @@ -719,6 +719,15 @@ "default": 2 } }, + { + "long": "fraction-ndigits", + "desc": "Set the number of digits to keep after the decimal point when printing ordinary fraction numbers", + "remark": "If negative, the number of digits will be automatically determined based on the value", + "arg": { + "type": "num", + "default": -1 + } + }, { "long": "temp-unit", "desc": "Set the temperature unit", diff --git a/src/detection/battery/battery_windows.c b/src/detection/battery/battery_windows.c index 101bd6dea7..dde78d2177 100644 --- a/src/detection/battery/battery_windows.c +++ b/src/detection/battery/battery_windows.c @@ -8,40 +8,31 @@ #undef WIN32_LEAN_AND_MEAN #include -#include #include #include +#include #include -static inline void wrapSetupDiDestroyDeviceInfoList(HDEVINFO* hdev) +static const char* detectWithCmApi(FFBatteryOptions* options, FFlist* results) { - if(*hdev) - SetupDiDestroyDeviceInfoList(*hdev); -} + //https://learn.microsoft.com/en-us/windows-hardware/drivers/install/using-device-interfaces + ULONG cchDeviceInterfaces = 0; + CONFIGRET cr = CM_Get_Device_Interface_List_SizeW(&cchDeviceInterfaces, (LPGUID)&GUID_DEVCLASS_BATTERY, NULL, CM_GET_DEVICE_INTERFACE_LIST_PRESENT); + if (cr != CR_SUCCESS) + return "CM_Get_Device_Interface_List_SizeW() failed"; -static const char* detectWithSetupApi(FFBatteryOptions* options, FFlist* results) -{ - //https://learn.microsoft.com/en-us/windows/win32/power/enumerating-battery-devices - HDEVINFO hdev __attribute__((__cleanup__(wrapSetupDiDestroyDeviceInfoList))) = - SetupDiGetClassDevsW(&GUID_DEVCLASS_BATTERY, 0, 0, DIGCF_PRESENT | DIGCF_DEVICEINTERFACE); - if(hdev == INVALID_HANDLE_VALUE) - return "SetupDiGetClassDevsW(&GUID_DEVCLASS_BATTERY) failed"; - - SP_DEVICE_INTERFACE_DATA did = { .cbSize = sizeof(did) }; - for(DWORD idev = 0; SetupDiEnumDeviceInterfaces(hdev, NULL, &GUID_DEVCLASS_BATTERY, idev, &did); idev++) - { - DWORD cbRequired = 0; - SetupDiGetDeviceInterfaceDetailW(hdev, &did, NULL, 0, &cbRequired, NULL); //Fail with not enough buffer - SP_DEVICE_INTERFACE_DETAIL_DATA_W* FF_AUTO_FREE pdidd = (SP_DEVICE_INTERFACE_DETAIL_DATA_W*)malloc(cbRequired); - if(!pdidd) - break; //Out of memory - - pdidd->cbSize = sizeof(*pdidd); - if(!SetupDiGetDeviceInterfaceDetailW(hdev, &did, pdidd, cbRequired, &cbRequired, NULL)) - continue; + if (cchDeviceInterfaces <= 1) + return NULL; // Not found + + wchar_t* FF_AUTO_FREE mszDeviceInterfaces = (wchar_t*)malloc(cchDeviceInterfaces * sizeof(wchar_t)); + cr = CM_Get_Device_Interface_ListW((LPGUID)&GUID_DEVCLASS_BATTERY, NULL, mszDeviceInterfaces, cchDeviceInterfaces, CM_GET_DEVICE_INTERFACE_LIST_PRESENT); + if (cr != CR_SUCCESS) + return "CM_Get_Device_Interface_ListW() failed"; + for (const wchar_t* pDeviceInterface = mszDeviceInterfaces; *pDeviceInterface; pDeviceInterface += wcslen(pDeviceInterface) + 1) + { HANDLE FF_AUTO_CLOSE_FD hBattery = - CreateFileW(pdidd->DevicePath, GENERIC_READ, FILE_SHARE_READ, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL); + CreateFileW(pDeviceInterface, GENERIC_READ, FILE_SHARE_READ, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL); if(hBattery == INVALID_HANDLE_VALUE) continue; @@ -99,7 +90,7 @@ static const char* detectWithSetupApi(FFBatteryOptions* options, FFlist* results bqi.InformationLevel = BatteryManufactureDate; BATTERY_MANUFACTURE_DATE date; if(DeviceIoControl(hBattery, IOCTL_BATTERY_QUERY_INFORMATION, &bqi, sizeof(bqi), &date, sizeof(date), &dwOut, NULL)) - ffStrbufSetF(&battery->manufactureDate, "%.4d-%.2d-%.2d", date.Year + 1900, date.Month, date.Day); + ffStrbufSetF(&battery->manufactureDate, "%.4d-%.2d-%.2d", date.Year < 1000 ? date.Year + 1900 : date.Year, date.Month, date.Day); } { @@ -131,7 +122,7 @@ static const char* detectWithSetupApi(FFBatteryOptions* options, FFlist* results { BATTERY_STATUS bs; BATTERY_WAIT_STATUS bws = { .BatteryTag = bqi.BatteryTag }; - if(DeviceIoControl(hBattery, IOCTL_BATTERY_QUERY_STATUS, &bws, sizeof(bws), &bs, sizeof(bs), &dwOut, NULL) && bs.Capacity != BATTERY_UNKNOWN_CAPACITY) + if(DeviceIoControl(hBattery, IOCTL_BATTERY_QUERY_STATUS, &bws, sizeof(bws), &bs, sizeof(bs), &dwOut, NULL) && bs.Capacity != BATTERY_UNKNOWN_CAPACITY && bi.FullChargedCapacity != 0) battery->capacity = bs.Capacity * 100.0 / bi.FullChargedCapacity; else battery->capacity = 0; @@ -271,6 +262,6 @@ static const char* detectWithNtApi(FF_MAYBE_UNUSED FFBatteryOptions* options, FF const char* ffDetectBattery(FFBatteryOptions* options, FFlist* results) { return options->useSetupApi - ? detectWithSetupApi(options, results) + ? detectWithCmApi(options, results) : detectWithNtApi(options, results); } diff --git a/src/detection/disk/disk_linux.c b/src/detection/disk/disk_linux.c index ae3c688534..0076c21f62 100644 --- a/src/detection/disk/disk_linux.c +++ b/src/detection/disk/disk_linux.c @@ -283,7 +283,7 @@ const char* ffDetectDisksImpl(FFDiskOptions* options, FFlist* disks) while((device = getmntent(mountsFile))) { - if (__builtin_expect(options->folders.length, 0)) + if (__builtin_expect(options->folders.length > 0, false)) { if (!ffDiskMatchMountpoint(&options->folders, device->mnt_dir)) continue; diff --git a/src/detection/gpu/gpu_apple.m b/src/detection/gpu/gpu_apple.m index f3f9c98e7e..b4937fbf3f 100644 --- a/src/detection/gpu/gpu_apple.m +++ b/src/detection/gpu/gpu_apple.m @@ -71,7 +71,7 @@ else if ([device supportsFamily:MTLGPUFamilyCommon1]) ffStrbufSetStatic(&gpu->platformApi, "Metal Common 1"); - gpu->type = device.location == MTLDeviceLocationBuiltIn ? FF_GPU_TYPE_INTEGRATED : FF_GPU_TYPE_DISCRETE; + gpu->type = device.hasUnifiedMemory ? FF_GPU_TYPE_INTEGRATED : FF_GPU_TYPE_DISCRETE; gpu->index = (uint32_t) device.locationNumber; if (device.hasUnifiedMemory && device.recommendedMaxWorkingSetSize > 0) diff --git a/src/detection/gpu/gpu_drm.c b/src/detection/gpu/gpu_drm.c index 192364568e..5ddea2799f 100644 --- a/src/detection/gpu/gpu_drm.c +++ b/src/detection/gpu/gpu_drm.c @@ -305,7 +305,7 @@ const char* ffDrmDetectAsahi(FFGPUResult* gpu, int fd) 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, + .pointer = (uintptr_t) ¶msGlobal, .size = sizeof(paramsGlobal), }) >= 0) { diff --git a/src/detection/gpu/gpu_intel.c b/src/detection/gpu/gpu_intel.c index c440c72074..59f2689302 100644 --- a/src/detection/gpu/gpu_intel.c +++ b/src/detection/gpu/gpu_intel.c @@ -89,19 +89,19 @@ const char* ffDetectIntelGpuInfo(const FFGpuDriverCondition* cond, FFGpuDriverRe if (properties.device_type != CTL_DEVICE_TYPE_GRAPHICS) continue; - if (cond->type & FF_GPU_DRIVER_CONDITION_TYPE_LUID) + if (cond->type & FF_GPU_DRIVER_CONDITION_TYPE_BUS_ID) { - if (cond->luid == deviceId) + if (cond->pciBusId.bus == properties.adapter_bdf.bus && + cond->pciBusId.device == properties.adapter_bdf.device && + cond->pciBusId.func == properties.adapter_bdf.function) { device = devices[iDev]; break; } } - else if (cond->type & FF_GPU_DRIVER_CONDITION_TYPE_BUS_ID) + else if (cond->type & FF_GPU_DRIVER_CONDITION_TYPE_LUID) { - if (cond->pciBusId.bus == properties.adapter_bdf.bus && - cond->pciBusId.device == properties.adapter_bdf.device && - cond->pciBusId.func == properties.adapter_bdf.function) + if (cond->luid == deviceId) { device = devices[iDev]; break; diff --git a/src/detection/gpu/gpu_nvidia.c b/src/detection/gpu/gpu_nvidia.c index f555eb9596..9e9eb1e126 100644 --- a/src/detection/gpu/gpu_nvidia.c +++ b/src/detection/gpu/gpu_nvidia.c @@ -29,11 +29,12 @@ struct FFNvapiData { FF_LIBRARY_SYMBOL(nvapi_Unload) FF_LIBRARY_SYMBOL(nvapi_EnumPhysicalGPUs) FF_LIBRARY_SYMBOL(nvapi_GPU_GetRamType) + FF_LIBRARY_SYMBOL(nvapi_GPU_GetGPUType) bool inited; } nvapiData; -static const char* detectMemTypeByNvapi(FFGpuDriverResult* result) +static const char* detectMoreByNvapi(FFGpuDriverResult* result) { if (!nvapiData.inited) { @@ -55,6 +56,7 @@ static const char* detectMemTypeByNvapi(FFGpuDriverResult* result) FF_NVAPI_INTERFACE(nvapi_Unload, NVAPI_INTERFACE_OFFSET_UNLOAD) FF_NVAPI_INTERFACE(nvapi_EnumPhysicalGPUs, NVAPI_INTERFACE_OFFSET_ENUM_PHYSICAL_GPUS) FF_NVAPI_INTERFACE(nvapi_GPU_GetRamType, NVAPI_INTERFACE_OFFSET_GPU_GET_RAM_TYPE) + FF_NVAPI_INTERFACE(nvapi_GPU_GetGPUType, NVAPI_INTERFACE_OFFSET_GPU_GET_GPU_TYPE) #undef FF_NVAPI_INTERFACE if (ffnvapi_Initialize() < 0) @@ -62,6 +64,7 @@ static const char* detectMemTypeByNvapi(FFGpuDriverResult* result) nvapiData.ffnvapi_EnumPhysicalGPUs = ffnvapi_EnumPhysicalGPUs; nvapiData.ffnvapi_GPU_GetRamType = ffnvapi_GPU_GetRamType; + nvapiData.ffnvapi_GPU_GetGPUType = ffnvapi_GPU_GetGPUType; nvapiData.ffnvapi_Unload = ffnvapi_Unload; atexit((void*) ffnvapi_Unload); @@ -86,36 +89,53 @@ static const char* detectMemTypeByNvapi(FFGpuDriverResult* result) NvPhysicalGpuHandle gpuHandle = handles[gpuIndex]; NvApiGPUMemoryType memType; - if (nvapiData.ffnvapi_GPU_GetRamType(gpuHandle, &memType) < 0) - return "NvAPI_GPU_GetRamType() failed"; + if (result->memoryType && nvapiData.ffnvapi_GPU_GetRamType(gpuHandle, &memType) == 0) + { + switch (memType) + { + #define FF_NVAPI_MEMORY_TYPE(type) \ + case NVAPI_GPU_MEMORY_TYPE_##type: \ + ffStrbufSetStatic(result->memoryType, #type); \ + break; + FF_NVAPI_MEMORY_TYPE(UNKNOWN) + FF_NVAPI_MEMORY_TYPE(SDRAM) + FF_NVAPI_MEMORY_TYPE(DDR1) + FF_NVAPI_MEMORY_TYPE(DDR2) + FF_NVAPI_MEMORY_TYPE(GDDR2) + FF_NVAPI_MEMORY_TYPE(GDDR3) + FF_NVAPI_MEMORY_TYPE(GDDR4) + FF_NVAPI_MEMORY_TYPE(DDR3) + FF_NVAPI_MEMORY_TYPE(GDDR5) + FF_NVAPI_MEMORY_TYPE(LPDDR2) + FF_NVAPI_MEMORY_TYPE(GDDR5X) + FF_NVAPI_MEMORY_TYPE(LPDDR3) + FF_NVAPI_MEMORY_TYPE(LPDDR4) + FF_NVAPI_MEMORY_TYPE(LPDDR5) + FF_NVAPI_MEMORY_TYPE(GDDR6) + FF_NVAPI_MEMORY_TYPE(GDDR6X) + FF_NVAPI_MEMORY_TYPE(GDDR7) + #undef FF_NVAPI_MEMORY_TYPE + default: + ffStrbufSetF(result->memoryType, "Unknown (%d)", memType); + break; + } + } - switch (memType) + NvApiGPUType gpuType; + if (result->type && nvapiData.ffnvapi_GPU_GetGPUType(gpuHandle, &gpuType) == 0) { - #define FF_NVAPI_MEMORY_TYPE(type) \ - case NVAPI_GPU_MEMORY_TYPE_##type: \ - ffStrbufSetStatic(result->memoryType, #type); \ + switch (gpuType) + { + case NV_SYSTEM_TYPE_IGPU: + *result->type = FF_GPU_TYPE_INTEGRATED; break; - FF_NVAPI_MEMORY_TYPE(UNKNOWN) - FF_NVAPI_MEMORY_TYPE(SDRAM) - FF_NVAPI_MEMORY_TYPE(DDR1) - FF_NVAPI_MEMORY_TYPE(DDR2) - FF_NVAPI_MEMORY_TYPE(GDDR2) - FF_NVAPI_MEMORY_TYPE(GDDR3) - FF_NVAPI_MEMORY_TYPE(GDDR4) - FF_NVAPI_MEMORY_TYPE(DDR3) - FF_NVAPI_MEMORY_TYPE(GDDR5) - FF_NVAPI_MEMORY_TYPE(LPDDR2) - FF_NVAPI_MEMORY_TYPE(GDDR5X) - FF_NVAPI_MEMORY_TYPE(LPDDR3) - FF_NVAPI_MEMORY_TYPE(LPDDR4) - FF_NVAPI_MEMORY_TYPE(LPDDR5) - FF_NVAPI_MEMORY_TYPE(GDDR6) - FF_NVAPI_MEMORY_TYPE(GDDR6X) - FF_NVAPI_MEMORY_TYPE(GDDR7) - #undef FF_NVAPI_MEMORY_TYPE - default: - ffStrbufSetF(result->memoryType, "Unknown (%d)", memType); - break; + case NV_SYSTEM_TYPE_DGPU: + *result->type = FF_GPU_TYPE_DISCRETE; + break; + default: + *result->type = FF_GPU_TYPE_UNKNOWN; + break; + } } return NULL; @@ -222,13 +242,13 @@ const char* ffDetectNvidiaGpuInfo(const FFGpuDriverCondition* cond, FFGpuDriverR { *result.index = value; #ifdef _WIN32 + // Don't bother loading nvapi for GPU type detection only if (result.memoryType) - detectMemTypeByNvapi(&result); + detectMoreByNvapi(&result); #endif } } - if (result.temp) { uint32_t value; diff --git a/src/detection/gpu/gpu_windows.c b/src/detection/gpu/gpu_windows.c index 851ef5e4b4..21dae1dc84 100644 --- a/src/detection/gpu/gpu_windows.c +++ b/src/detection/gpu/gpu_windows.c @@ -1,18 +1,15 @@ #include "gpu.h" +#include "common/library.h" #include "detection/gpu/gpu_driver_specific.h" #include "util/windows/unicode.h" #include "util/windows/registry.h" +#include "util/mallocHelper.h" #define INITGUID -#include -#include -#include - -static inline void wrapSetupDiDestroyDeviceInfoList(HDEVINFO* hdev) -{ - if(*hdev) - SetupDiDestroyDeviceInfoList(*hdev); -} +#include +#include +#include +#include "util/windows/nt.h" #define FF_EMPTY_GUID_STR L"{00000000-0000-0000-0000-000000000000}" enum { FF_GUID_STRLEN = sizeof(FF_EMPTY_GUID_STR) / sizeof(wchar_t) - 1 }; @@ -24,21 +21,38 @@ const uint32_t regDriverKeyPrefixLength = (uint32_t) __builtin_strlen("SYSTEM\\C const char* ffDetectGPUImpl(FF_MAYBE_UNUSED const FFGPUOptions* options, FFlist* gpus) { - HDEVINFO hdev __attribute__((__cleanup__(wrapSetupDiDestroyDeviceInfoList))) = - SetupDiGetClassDevsW(&GUID_DEVCLASS_DISPLAY, NULL, NULL, DIGCF_PRESENT); + ULONG devInfListSize = 0; + if (CM_Get_Device_Interface_List_SizeW(&devInfListSize, (LPGUID)&GUID_DEVINTERFACE_DISPLAY_ADAPTER, NULL, CM_GET_DEVICE_INTERFACE_LIST_PRESENT) != CR_SUCCESS || devInfListSize <= 1) + return "No display devices found"; + + FF_AUTO_FREE DEVINSTID_W devInfList = malloc(devInfListSize * sizeof(*devInfList)); - if(hdev == INVALID_HANDLE_VALUE) - return "SetupDiGetClassDevsW(&GUID_DEVCLASS_DISPLAY) failed"; + if (CM_Get_Device_Interface_ListW((LPGUID)&GUID_DEVINTERFACE_DISPLAY_ADAPTER, NULL, devInfList, devInfListSize, CM_GET_DEVICE_INTERFACE_LIST_PRESENT) != CR_SUCCESS) + return "CM_Get_Device_Interface_ListW failed"; - SP_DEVINFO_DATA did = { .cbSize = sizeof(did) }; - for (DWORD idev = 0; SetupDiEnumDeviceInfo(hdev, idev, &did); ++idev) + for (wchar_t* devInf = devInfList; *devInf; devInf += wcslen(devInf) + 1) { + if (wcsncmp(devInf, L"\\\\?\\ROOT#BasicDisplay#", 22) == 0) + continue; // Skip Microsoft Basic Display Adapter + + DEVINST devInst = 0; + wchar_t buffer[256]; + ULONG bufferLen = 0; + + { + DEVPROPTYPE propertyType; + bufferLen = sizeof(buffer); + if (CM_Get_Device_Interface_PropertyW(devInf, &DEVPKEY_Device_InstanceId, &propertyType, (PBYTE) buffer, &bufferLen, 0) != CR_SUCCESS || + CM_Locate_DevNodeW(&devInst, buffer, CM_LOCATE_DEVNODE_NORMAL) != CR_SUCCESS) + continue; + } + FFGPUResult* gpu = (FFGPUResult*)ffListAdd(gpus); ffStrbufInit(&gpu->vendor); ffStrbufInit(&gpu->name); ffStrbufInit(&gpu->driver); ffStrbufInit(&gpu->memoryType); - ffStrbufInitStatic(&gpu->platformApi, "SetupAPI"); + ffStrbufInitStatic(&gpu->platformApi, "CM API"); gpu->index = FF_GPU_INDEX_UNSET; gpu->temperature = FF_GPU_TEMP_UNSET; gpu->coreCount = FF_GPU_CORE_COUNT_UNSET; @@ -48,86 +62,91 @@ const char* ffDetectGPUImpl(FF_MAYBE_UNUSED const FFGPUOptions* options, FFlist* gpu->deviceId = 0; gpu->frequency = FF_GPU_FREQUENCY_UNSET; + unsigned vendorId = 0, deviceId = 0, subSystemId = 0, revId = 0; + if (swscanf(buffer, L"PCI\\VEN_%x&DEV_%x&SUBSYS_%x&REV_%x", &vendorId, &deviceId, &subSystemId, &revId) == 4) + ffStrbufSetStatic(&gpu->vendor, ffGPUGetVendorString(vendorId)); + uint32_t pciBus = 0, pciAddr = 0, pciDev = 0, pciFunc = 0; - if (SetupDiGetDeviceRegistryPropertyW(hdev, &did, SPDRP_BUSNUMBER, NULL, (PBYTE) &pciBus, sizeof(pciBus), NULL) && - SetupDiGetDeviceRegistryPropertyW(hdev, &did, SPDRP_ADDRESS, NULL, (PBYTE) &pciAddr, sizeof(pciAddr), NULL)) + + ULONG pciBufLen = sizeof(pciBus); + if (CM_Get_DevNode_Registry_PropertyW(devInst, CM_DRP_BUSNUMBER, NULL, &pciBus, &pciBufLen, 0) == CR_SUCCESS) { - pciDev = (pciAddr >> 16) & 0xFFFF; - pciFunc = pciAddr & 0xFFFF; - gpu->deviceId = (pciBus * 1000ull) + (pciDev * 10ull) + pciFunc; - pciAddr = 1; // Set to 1 to indicate that the device is a PCI device + pciBufLen = sizeof(pciAddr); + if (CM_Get_DevNode_Registry_PropertyW(devInst, CM_DRP_ADDRESS, NULL, &pciAddr, &pciBufLen, 0) == CR_SUCCESS) + { + pciDev = (pciAddr >> 16) & 0xFFFF; + pciFunc = pciAddr & 0xFFFF; + gpu->deviceId = (pciBus * 1000ull) + (pciDev * 10ull) + pciFunc; + pciAddr = 1; // Set to 1 to indicate that the device is a PCI device + } } - wchar_t buffer[256]; - uint64_t adapterLuid = 0; - FF_HKEY_AUTO_DESTROY hVideoIdKey = SetupDiOpenDevRegKey(hdev, &did, DICS_FLAG_GLOBAL, 0, DIREG_DEV, KEY_QUERY_VALUE); - if (!hVideoIdKey) continue; - DWORD bufferLen = sizeof(buffer); - if (RegGetValueW(hVideoIdKey, NULL, L"VideoID", RRF_RT_REG_SZ, NULL, buffer, &bufferLen) == ERROR_SUCCESS && - bufferLen == (FF_GUID_STRLEN + 1) * sizeof(wchar_t)) + FF_HKEY_AUTO_DESTROY hVideoIdKey = NULL; + if (CM_Open_DevNode_Key(devInst, KEY_QUERY_VALUE, 0, RegDisposition_OpenExisting, &hVideoIdKey, CM_REGISTRY_HARDWARE) == CR_SUCCESS) { - wmemcpy(regDirectxKey + regDirectxKeyPrefixLength, buffer, FF_GUID_STRLEN); - FF_HKEY_AUTO_DESTROY hDirectxKey = NULL; - if (ffRegOpenKeyForRead(HKEY_LOCAL_MACHINE, regDirectxKey, &hDirectxKey, NULL)) + bufferLen = sizeof(buffer); + if (RegGetValueW(hVideoIdKey, NULL, L"VideoID", RRF_RT_REG_SZ, NULL, buffer, &bufferLen) == ERROR_SUCCESS && + bufferLen == (FF_GUID_STRLEN + 1) * sizeof(wchar_t)) { - uint32_t vendorId = 0; - if(ffRegReadUint(hDirectxKey, L"VendorId", &vendorId, NULL) && vendorId) - ffStrbufSetStatic(&gpu->vendor, ffGPUGetVendorString(vendorId)); - - if (gpu->vendor.chars == FF_GPU_VENDOR_NAME_INTEL) - gpu->type = gpu->deviceId == 20 ? FF_GPU_TYPE_INTEGRATED : FF_GPU_TYPE_DISCRETE; - - uint64_t dedicatedVideoMemory = 0; - if(ffRegReadUint64(hDirectxKey, L"DedicatedVideoMemory", &dedicatedVideoMemory, NULL)) + wmemcpy(regDirectxKey + regDirectxKeyPrefixLength, buffer, FF_GUID_STRLEN); + FF_HKEY_AUTO_DESTROY hDirectxKey = NULL; + if (ffRegOpenKeyForRead(HKEY_LOCAL_MACHINE, regDirectxKey, &hDirectxKey, NULL)) { - if (gpu->type == FF_GPU_TYPE_UNKNOWN) - gpu->type = dedicatedVideoMemory >= 1024 * 1024 * 1024 ? FF_GPU_TYPE_DISCRETE : FF_GPU_TYPE_INTEGRATED; - } + if (gpu->vendor.length == 0) + { + uint32_t vendorId = 0; + if(ffRegReadUint(hDirectxKey, L"VendorId", &vendorId, NULL) && vendorId) + ffStrbufSetStatic(&gpu->vendor, ffGPUGetVendorString(vendorId)); + } - uint64_t dedicatedSystemMemory, sharedSystemMemory; - if(ffRegReadUint64(hDirectxKey, L"DedicatedSystemMemory", &dedicatedSystemMemory, NULL) && - ffRegReadUint64(hDirectxKey, L"SharedSystemMemory", &sharedSystemMemory, NULL)) - { - gpu->dedicated.total = dedicatedVideoMemory + dedicatedSystemMemory; - gpu->shared.total = sharedSystemMemory; - } + ffRegReadStrbuf(hDirectxKey, L"Description", &gpu->name, NULL); - if (ffRegReadUint64(hDirectxKey, L"AdapterLuid", &adapterLuid, NULL)) - { - if (!gpu->deviceId) gpu->deviceId = adapterLuid; - } + ffRegReadUint64(hDirectxKey, L"DedicatedVideoMemory", &gpu->dedicated.total, NULL); + if (ffRegReadUint64(hDirectxKey, L"DedicatedSystemMemory", &gpu->shared.total, NULL)) + { + uint64_t sharedSystemMemory = 0; + if (ffRegReadUint64(hDirectxKey, L"SharedSystemMemory", &sharedSystemMemory, NULL)) + gpu->shared.total += sharedSystemMemory; + } - uint32_t featureLevel = 0; - if(ffRegReadUint(hDirectxKey, L"MaxD3D12FeatureLevel", &featureLevel, NULL) && featureLevel) - ffStrbufSetF(&gpu->platformApi, "Direct3D 12.%u", (featureLevel & 0x0F00) >> 8); - else if(ffRegReadUint(hDirectxKey, L"MaxD3D11FeatureLevel", &featureLevel, NULL) && featureLevel) - ffStrbufSetF(&gpu->platformApi, "Direct3D 11.%u", (featureLevel & 0x0F00) >> 8); + if (ffRegReadUint64(hDirectxKey, L"AdapterLuid", &adapterLuid, NULL)) + { + if (!gpu->deviceId) gpu->deviceId = adapterLuid; + } - uint64_t driverVersion = 0; - if(ffRegReadUint64(hDirectxKey, L"DriverVersion", &driverVersion, NULL) && driverVersion) - { - ffStrbufSetF(&gpu->driver, "%u.%u.%u.%u", - (unsigned) (driverVersion >> 48) & 0xFFFF, - (unsigned) (driverVersion >> 32) & 0xFFFF, - (unsigned) (driverVersion >> 16) & 0xFFFF, - (unsigned) (driverVersion >> 0) & 0xFFFF - ); + uint32_t featureLevel = 0; + if(ffRegReadUint(hDirectxKey, L"MaxD3D12FeatureLevel", &featureLevel, NULL) && featureLevel) + ffStrbufSetF(&gpu->platformApi, "Direct3D 12.%u", (featureLevel & 0x0F00) >> 8); + else if(ffRegReadUint(hDirectxKey, L"MaxD3D11FeatureLevel", &featureLevel, NULL) && featureLevel) + ffStrbufSetF(&gpu->platformApi, "Direct3D 11.%u", (featureLevel & 0x0F00) >> 8); + + uint64_t driverVersion = 0; + if(ffRegReadUint64(hDirectxKey, L"DriverVersion", &driverVersion, NULL) && driverVersion) + { + ffStrbufSetF(&gpu->driver, "%u.%u.%u.%u", + (unsigned) (driverVersion >> 48) & 0xFFFF, + (unsigned) (driverVersion >> 32) & 0xFFFF, + (unsigned) (driverVersion >> 16) & 0xFFFF, + (unsigned) (driverVersion >> 0) & 0xFFFF + ); + } } } } - if (gpu->vendor.length == 0) + if (gpu->vendor.length == 0 || gpu->name.length == 0) { bufferLen = sizeof(buffer); - if (SetupDiGetDeviceRegistryPropertyW(hdev, &did, SPDRP_DRIVER, NULL, (PBYTE) buffer, sizeof(buffer), &bufferLen) && bufferLen == (FF_GUID_STRLEN + strlen("\\0000") + 1) * 2) + if (CM_Get_DevNode_Registry_PropertyW(devInst, CM_DRP_DRIVER, NULL, buffer, &bufferLen, 0) == CR_SUCCESS && + bufferLen == (FF_GUID_STRLEN + strlen("\\0000") + 1) * 2) { wmemcpy(regDriverKey + regDriverKeyPrefixLength, buffer, FF_GUID_STRLEN + strlen("\\0000")); FF_HKEY_AUTO_DESTROY hRegDriverKey = NULL; if (ffRegOpenKeyForRead(HKEY_LOCAL_MACHINE, regDriverKey, &hRegDriverKey, NULL)) { - if (ffRegReadStrbuf(hRegDriverKey, L"ProviderName", &gpu->vendor, NULL)) + if (gpu->vendor.length == 0 && ffRegReadStrbuf(hRegDriverKey, L"ProviderName", &gpu->vendor, NULL)) { if (ffStrbufContainS(&gpu->vendor, "Intel")) ffStrbufSetStatic(&gpu->vendor, FF_GPU_VENDOR_NAME_INTEL); @@ -136,6 +155,19 @@ const char* ffDetectGPUImpl(FF_MAYBE_UNUSED const FFGPUOptions* options, FFlist* else if (ffStrbufContainS(&gpu->vendor, "AMD") || ffStrbufContainS(&gpu->vendor, "ATI")) ffStrbufSetStatic(&gpu->vendor, FF_GPU_VENDOR_NAME_AMD); } + if (gpu->name.length == 0) + ffRegReadStrbuf(hRegDriverKey, L"DriverDesc", &gpu->name, NULL); + if (gpu->driver.length == 0) + ffRegReadStrbuf(hRegDriverKey, L"DriverVersion", &gpu->driver, NULL); + if (gpu->dedicated.total == FF_GPU_VMEM_SIZE_UNSET) + { + if (!ffRegReadUint64(hRegDriverKey, L"HardwareInformation.qwMemorySize", &gpu->dedicated.total, NULL)) + { + uint32_t memorySize = 0; + if (ffRegReadUint(hRegDriverKey, L"HardwareInformation.MemorySize", &memorySize, NULL)) + gpu->dedicated.total = memorySize; + } + } } } } @@ -143,18 +175,11 @@ const char* ffDetectGPUImpl(FF_MAYBE_UNUSED const FFGPUOptions* options, FFlist* __typeof__(&ffDetectNvidiaGpuInfo) detectFn; const char* dllName; - if (getDriverSpecificDetectionFn(gpu->vendor.chars, &detectFn, &dllName) && (options->temp || options->driverSpecific)) + if (options->driverSpecific && getDriverSpecificDetectionFn(gpu->vendor.chars, &detectFn, &dllName)) { - unsigned vendorId = 0, deviceId = 0, subSystemId = 0, revId = 0; - if (SetupDiGetDeviceRegistryPropertyW(hdev, &did, SPDRP_HARDWAREID, NULL, (PBYTE) buffer, sizeof(buffer), NULL)) - { - swscanf(buffer, L"PCI\\VEN_%x&DEV_%x&SUBSYS_%x&REV_%x", &vendorId, &deviceId, &subSystemId, &revId); - ffStrbufSetStatic(&gpu->vendor, ffGPUGetVendorString(vendorId)); - } - detectFn( &(FFGpuDriverCondition) { - .type = FF_GPU_DRIVER_CONDITION_TYPE_DEVICE_ID + .type = (deviceId > 0 ? FF_GPU_DRIVER_CONDITION_TYPE_DEVICE_ID : 0) | (adapterLuid > 0 ? FF_GPU_DRIVER_CONDITION_TYPE_LUID : 0) | (pciAddr > 0 ? FF_GPU_DRIVER_CONDITION_TYPE_BUS_ID : 0), .pciDeviceId = { @@ -189,9 +214,93 @@ const char* ffDetectGPUImpl(FF_MAYBE_UNUSED const FFGPUOptions* options, FFlist* if (!gpu->name.length) { - if (SetupDiGetDeviceRegistryPropertyW(hdev, &did, SPDRP_DEVICEDESC, NULL, (PBYTE) buffer, sizeof(buffer), NULL)) + bufferLen = sizeof(buffer); + if (CM_Get_DevNode_Registry_PropertyW(devInst, CM_DRP_DEVICEDESC, NULL, buffer, &bufferLen, 0) == CR_SUCCESS) ffStrbufSetWS(&gpu->name, buffer); } + + if (gpu->type == FF_GPU_TYPE_UNKNOWN && adapterLuid > 0) + { + HMODULE hgdi32 = GetModuleHandleW(L"gdi32.dll"); + if (hgdi32) + { + FF_LIBRARY_LOAD_SYMBOL_LAZY(hgdi32, D3DKMTOpenAdapterFromLuid); + if (ffD3DKMTOpenAdapterFromLuid) // Windows 8 and later + { + D3DKMT_OPENADAPTERFROMLUID openAdapterFromLuid = { .AdapterLuid = *(LUID*)&adapterLuid }; + if (NT_SUCCESS(ffD3DKMTOpenAdapterFromLuid(&openAdapterFromLuid))) + { + D3DKMT_ADAPTERTYPE adapterType = {}; + D3DKMT_QUERYADAPTERINFO queryAdapterInfo = { + .hAdapter = openAdapterFromLuid.hAdapter, + .Type = KMTQAITYPE_ADAPTERTYPE, + .pPrivateDriverData = &adapterType, + .PrivateDriverDataSize = sizeof(adapterType), + }; + if (NT_SUCCESS(D3DKMTQueryAdapterInfo(&queryAdapterInfo))) // Vista and later + { + if (adapterType.HybridDiscrete) + gpu->type = FF_GPU_TYPE_DISCRETE; + else if (adapterType.HybridIntegrated) + gpu->type = FF_GPU_TYPE_INTEGRATED; + } + + if (gpu->frequency == FF_GPU_FREQUENCY_UNSET) + { + for (ULONG nodeIdx = 0; ; nodeIdx++) + { + D3DKMT_NODEMETADATA nodeMetadata = { + .NodeOrdinalAndAdapterIndex = (0 << 16) | nodeIdx, + }; + queryAdapterInfo = (D3DKMT_QUERYADAPTERINFO) { + .hAdapter = openAdapterFromLuid.hAdapter, + .Type = KMTQAITYPE_NODEMETADATA, + .pPrivateDriverData = &nodeMetadata, + .PrivateDriverDataSize = sizeof(nodeMetadata), + }; + if (!NT_SUCCESS(D3DKMTQueryAdapterInfo(&queryAdapterInfo))) break; // Windows 10 and later + if (nodeMetadata.NodeData.EngineType != DXGK_ENGINE_TYPE_3D) continue; + + D3DKMT_QUERYSTATISTICS queryStatistics = { + .Type = D3DKMT_QUERYSTATISTICS_NODE2, + .AdapterLuid = *(LUID*)&adapterLuid, + .QueryNode2 = { .PhysicalAdapterIndex = 0, .NodeOrdinal = (UINT16) nodeIdx }, + }; + if (NT_SUCCESS(D3DKMTQueryStatistics(&queryStatistics))) // Windows 11 (22H2) and later + { + gpu->frequency = (uint32_t) (queryStatistics.QueryResult.NodeInformation.NodePerfData.MaxFrequency / 1000 / 1000); + break; + } + } + } + + D3DKMT_CLOSEADAPTER closeAdapter = { .hAdapter = openAdapterFromLuid.hAdapter }; + (void) D3DKMTCloseAdapter(&closeAdapter); + openAdapterFromLuid.hAdapter = 0; + } + + if (options->temp && gpu->temperature != gpu->temperature) + { + D3DKMT_QUERYSTATISTICS queryStatistics = { + .Type = D3DKMT_QUERYSTATISTICS_PHYSICAL_ADAPTER, + .AdapterLuid = *(LUID*)&adapterLuid, + .QueryPhysAdapter = { .PhysicalAdapterIndex = 0 }, + }; + if (NT_SUCCESS(D3DKMTQueryStatistics(&queryStatistics)) && + queryStatistics.QueryResult.PhysAdapterInformation.AdapterPerfData.Temperature != 0) + gpu->temperature = queryStatistics.QueryResult.PhysAdapterInformation.AdapterPerfData.Temperature / 10.0; + } + } + } + } + + if (gpu->type == FF_GPU_TYPE_UNKNOWN) + { + if (gpu->vendor.chars == FF_GPU_VENDOR_NAME_INTEL) + gpu->type = gpu->deviceId == 20 ? FF_GPU_TYPE_INTEGRATED : FF_GPU_TYPE_DISCRETE; + else if (gpu->dedicated.total != FF_GPU_VMEM_SIZE_UNSET) + gpu->type = gpu->dedicated.total >= 1024 * 1024 * 1024 ? FF_GPU_TYPE_DISCRETE : FF_GPU_TYPE_INTEGRATED; + } } return NULL; diff --git a/src/detection/gpu/nvapi.h b/src/detection/gpu/nvapi.h index e616d3e5e8..bd9ac7724e 100644 --- a/src/detection/gpu/nvapi.h +++ b/src/detection/gpu/nvapi.h @@ -23,6 +23,13 @@ typedef enum NvApiGPUMemoryType NVAPI_GPU_MEMORY_TYPE_GDDR7, } NvApiGPUMemoryType; +typedef enum +{ + NV_SYSTEM_TYPE_GPU_UNKNOWN = 0, + NV_SYSTEM_TYPE_IGPU = 1, // Integrated + NV_SYSTEM_TYPE_DGPU = 2, // Discrete +} NvApiGPUType; + typedef int NvAPI_Status; // 0 = success; < 0 = error typedef struct NvPhysicalGpuHandle* NvPhysicalGpuHandle; @@ -32,6 +39,7 @@ typedef enum NVAPI_INTERFACE_OFFSET_UNLOAD = 0xD22BDD7E, NVAPI_INTERFACE_OFFSET_ENUM_PHYSICAL_GPUS = 0xE5AC921F, NVAPI_INTERFACE_OFFSET_GPU_GET_RAM_TYPE = 0x57F7CAAC, + NVAPI_INTERFACE_OFFSET_GPU_GET_GPU_TYPE = 0xC33BAEB1, NVAPI_INTERFACE_OFFSET_FORCE_UINT32 = 0xFFFFFFFF } NvApiInterfaceOffsets; @@ -42,3 +50,4 @@ extern NvAPI_Status nvapi_Initialize(void); extern NvAPI_Status nvapi_Unload(void); extern NvAPI_Status nvapi_EnumPhysicalGPUs(NvPhysicalGpuHandle* handles, int* count); extern NvAPI_Status nvapi_GPU_GetRamType(NvPhysicalGpuHandle handle, NvApiGPUMemoryType* memtype); +extern NvAPI_Status nvapi_GPU_GetGPUType(NvPhysicalGpuHandle handle, NvApiGPUType* gpuType); diff --git a/src/detection/os/os_linux.c b/src/detection/os/os_linux.c index b87a9ce7bc..0774b9816b 100644 --- a/src/detection/os/os_linux.c +++ b/src/detection/os/os_linux.c @@ -291,6 +291,21 @@ FF_MAYBE_UNUSED static bool detectDebianDerived(FFOSResult* result) return false; } +FF_MAYBE_UNUSED static bool detectFedoraVariant(FFOSResult* result) +{ + if (ffStrbufEqualS(&result->variantID, "coreos") + || ffStrbufEqualS(&result->variantID, "kinoite") + || ffStrbufEqualS(&result->variantID, "sericea") + || ffStrbufEqualS(&result->variantID, "silverblue")) + { + ffStrbufAppendC(&result->id, '-'); + ffStrbufAppend(&result->id, &result->variantID); + ffStrbufSetStatic(&result->idLike, "fedora"); + return true; + } + return false; +} + static void detectOS(FFOSResult* os) { #ifdef FF_CUSTOM_OS_RELEASE_PATH @@ -341,13 +356,15 @@ void ffDetectOSImpl(FFOSResult* os) detectOS(os); #ifdef __linux__ - if(ffStrbufIgnCaseEqualS(&os->id, "ubuntu")) + if(ffStrbufEqualS(&os->id, "ubuntu")) getUbuntuFlavour(os); - else if(ffStrbufIgnCaseEqualS(&os->id, "debian")) + else if(ffStrbufEqualS(&os->id, "debian")) { if (!detectDebianDerived(os)) getDebianVersion(os); } + else if(ffStrbufEqualS(&os->id, "fedora")) + detectFedoraVariant(os); else if(ffStrbufEqualS(&os->id, "linuxmint")) { if (ffStrbufEqualS(&os->name, "LMDE")) diff --git a/src/detection/packages/packages_linux.c b/src/detection/packages/packages_linux.c index 844b3de914..19543a8754 100644 --- a/src/detection/packages/packages_linux.c +++ b/src/detection/packages/packages_linux.c @@ -646,33 +646,30 @@ void ffDetectPackagesImpl(FFPackagesResult* result, FFPackagesOptions* options) #endif ffStrbufSet(&baseDir, &instance.state.platform.homeDir); + if (!(options->disabled & FF_PACKAGES_FLAG_NIX_BIT)) { - // check if ~/.nix-profile exists - FF_STRBUF_AUTO_DESTROY profilePath = ffStrbufCreateCopy(&baseDir); - ffStrbufAppendS(&profilePath, ".nix-profile"); - if (ffPathExists(profilePath.chars, FF_PATHTYPE_DIRECTORY)) - { - result->nixUser += getNixPackages(&baseDir, ".nix-profile"); - } + // Count packages from $HOME/.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)) + // Check in $XDG_STATE_HOME/nix/profile + FF_STRBUF_AUTO_DESTROY stateHome = ffStrbufCreate(); + const char* stateHomeEnv = getenv("XDG_STATE_HOME"); + if (ffStrSet(stateHomeEnv)) { - ffStrbufSetS(&stateDir, stateHome); - ffStrbufEnsureEndsWithC(&stateDir, '/'); + ffStrbufSetS(&stateHome, stateHomeEnv); + ffStrbufEnsureEndsWithC(&stateHome, '/'); } else { - ffStrbufSet(&stateDir, &instance.state.platform.homeDir); - ffStrbufAppendS(&stateDir, ".local/state/"); + ffStrbufSet(&stateHome, &instance.state.platform.homeDir); + ffStrbufAppendS(&stateHome, ".local/state/"); } + result->nixUser += getNixPackages(&stateHome, "nix/profile"); - ffStrbufSet(&profilePath, &stateDir); - ffStrbufAppendS(&profilePath, "nix/profile"); - result->nixUser += getNixPackages(&stateDir, "nix/profile"); + // Check in /etc/profiles/per-user/$USER + FF_STRBUF_AUTO_DESTROY userPkgsDir = ffStrbufCreateStatic("/etc/profiles/per-user/"); + result->nixUser += getNixPackages(&userPkgsDir, instance.state.platform.userName.chars); } if (!(options->disabled & FF_PACKAGES_FLAG_GUIX_BIT)) diff --git a/src/detection/physicaldisk/physicaldisk_apple.c b/src/detection/physicaldisk/physicaldisk_apple.c index aba4cf4ddf..ce125533d7 100644 --- a/src/detection/physicaldisk/physicaldisk_apple.c +++ b/src/detection/physicaldisk/physicaldisk_apple.c @@ -113,9 +113,9 @@ const char* ffDetectPhysicalDisk(FFlist* result, FFPhysicalDiskOptions* options) if (deviceCharacteristics) { ffCfDictGetString(deviceCharacteristics, CFSTR(kIOPropertyProductSerialNumberKey), &device->serial); - ffStrbufTrim(&device->serial, ' '); + ffStrbufTrimSpace(&device->serial); ffCfDictGetString(deviceCharacteristics, CFSTR(kIOPropertyProductRevisionLevelKey), &device->revision); - ffStrbufTrim(&device->revision, ' '); + ffStrbufTrimRightSpace(&device->revision); CFStringRef mediumType = (CFStringRef) CFDictionaryGetValue(deviceCharacteristics, CFSTR(kIOPropertyMediumTypeKey)); if (mediumType) diff --git a/src/detection/physicaldisk/physicaldisk_bsd.c b/src/detection/physicaldisk/physicaldisk_bsd.c index 562fbb3cfd..9bb4669dd0 100644 --- a/src/detection/physicaldisk/physicaldisk_bsd.c +++ b/src/detection/physicaldisk/physicaldisk_bsd.c @@ -65,7 +65,7 @@ const char* ffDetectPhysicalDisk(FFlist* result, FFPhysicalDiskOptions* options) FFPhysicalDiskResult* device = (FFPhysicalDiskResult*) ffListAdd(result); ffStrbufInitF(&device->devPath, "/dev/%s", provider->lg_name); ffStrbufInitMove(&device->serial, &identifier); - ffStrbufTrim(&device->serial, ' '); + ffStrbufTrimSpace(&device->serial); ffStrbufInit(&device->revision); ffStrbufInit(&device->interconnect); switch (snapIter->device_type & DEVSTAT_TYPE_IF_MASK) diff --git a/src/detection/physicaldisk/physicaldisk_linux.c b/src/detection/physicaldisk/physicaldisk_linux.c index 11923f4d4f..f4f46a0fe3 100644 --- a/src/detection/physicaldisk/physicaldisk_linux.c +++ b/src/detection/physicaldisk/physicaldisk_linux.c @@ -153,7 +153,7 @@ static void parsePhysicalDisk(int dfd, const char* devName, FFPhysicalDiskOption { ffStrbufInit(&device->serial); if (ffReadFileBufferRelative(devfd, "serial", &device->serial)) - ffStrbufTrim(&device->serial, ' '); + ffStrbufTrimSpace(&device->serial); } { diff --git a/src/detection/physicaldisk/physicaldisk_sunos.c b/src/detection/physicaldisk/physicaldisk_sunos.c index f3b4b3de89..fb14c889eb 100644 --- a/src/detection/physicaldisk/physicaldisk_sunos.c +++ b/src/detection/physicaldisk/physicaldisk_sunos.c @@ -36,9 +36,15 @@ static int walkDevTree(di_node_t node, di_minor_t minor, struct FFWalkTreeBundle char* buf; if (di_prop_lookup_strings(DDI_DEV_T_ANY, node, "inquiry-serial-no", &buf) > 0) + { ffStrbufSetS(&device->serial, buf); + ffStrbufTrimSpace(&device->serial); + } if (di_prop_lookup_strings(DDI_DEV_T_ANY, node, "inquiry-revision-id", &buf) > 0) + { ffStrbufSetS(&device->revision, buf); + ffStrbufTrimRightSpace(&device->revision); + } if (di_prop_lookup_strings(DDI_DEV_T_ANY, node, "class", &buf) > 0) ffStrbufSetS(&device->interconnect, buf); diff --git a/src/detection/physicaldisk/physicaldisk_windows.c b/src/detection/physicaldisk/physicaldisk_windows.c index 486f6aa181..0a6e76d588 100644 --- a/src/detection/physicaldisk/physicaldisk_windows.c +++ b/src/detection/physicaldisk/physicaldisk_windows.c @@ -62,14 +62,14 @@ static bool detectPhysicalDisk(const wchar_t* szDevice, FFlist* result, FFPhysic if (sdd->SerialNumberOffset != 0) { ffStrbufSetS(&device->serial, (const char*) sddBuffer + sdd->SerialNumberOffset); - ffStrbufTrim(&device->serial, ' '); + ffStrbufTrimSpace(&device->serial); } ffStrbufInit(&device->revision); if (sdd->ProductRevisionOffset != 0) { ffStrbufSetS(&device->revision, (const char*) sddBuffer + sdd->ProductRevisionOffset); - ffStrbufTrim(&device->revision, ' '); + ffStrbufTrimRightSpace(&device->revision); } device->type |= sdd->RemovableMedia ? FF_PHYSICALDISK_TYPE_REMOVABLE : FF_PHYSICALDISK_TYPE_FIXED; diff --git a/src/detection/sound/sound_apple.c b/src/detection/sound/sound_apple.c index 88881adc57..cd21912c0a 100644 --- a/src/detection/sound/sound_apple.c +++ b/src/detection/sound/sound_apple.c @@ -32,15 +32,6 @@ const char* ffDetectSound(FFlist* devices /* List of FFSoundDevice */) { AudioDeviceID deviceId = deviceIds[index]; - FF_CFTYPE_AUTO_RELEASE CFStringRef name = NULL; - dataSize = sizeof(name); - if(AudioObjectGetPropertyData(deviceId, &(AudioObjectPropertyAddress){ - kAudioObjectPropertyName, - kAudioObjectPropertyScopeOutput, - kAudioObjectPropertyElementMain - }, 0, NULL, &dataSize, &name) != kAudioHardwareNoError) - continue; - // Ignore input devices if(AudioObjectGetPropertyDataSize(deviceId, &(AudioObjectPropertyAddress){ kAudioDevicePropertyStreams, @@ -71,10 +62,31 @@ const char* ffDetectSound(FFlist* devices /* List of FFSoundDevice */) device->main = deviceId == mainDeviceId; device->active = false; device->volume = FF_SOUND_VOLUME_UNKNOWN; - ffStrbufInitF(&device->identifier, "%u", (unsigned) deviceId); + ffStrbufInit(&device->identifier); ffStrbufInit(&device->name); ffStrbufInitStatic(&device->platformApi, "Core Audio"); - ffCfStrGetString(name, &device->name); + + FF_CFTYPE_AUTO_RELEASE CFStringRef uid = NULL; + dataSize = sizeof(uid); + if(AudioObjectGetPropertyData(deviceId, &(AudioObjectPropertyAddress) { + kAudioDevicePropertyDeviceUID, + kAudioObjectPropertyScopeOutput, + kAudioObjectPropertyElementMain + }, 0, NULL, &dataSize, &uid) == kAudioHardwareNoError) + ffCfStrGetString(uid, &device->identifier); + else + ffStrbufAppendF(&device->identifier, "ID-%u", (unsigned) deviceId); + + FF_CFTYPE_AUTO_RELEASE CFStringRef name = NULL; + dataSize = sizeof(name); + if(AudioObjectGetPropertyData(deviceId, &(AudioObjectPropertyAddress){ + kAudioObjectPropertyName, + kAudioObjectPropertyScopeOutput, + kAudioObjectPropertyElementMain + }, 0, NULL, &dataSize, &name) == kAudioHardwareNoError) + ffCfStrGetString(name, &device->name); + else + ffStrbufSet(&device->name, &device->identifier); uint32_t muted; dataSize = sizeof(muted); diff --git a/src/logo/ascii/axos.txt b/src/logo/ascii/axos.txt new file mode 100644 index 0000000000..4cc4f83098 --- /dev/null +++ b/src/logo/ascii/axos.txt @@ -0,0 +1,16 @@ + โ–‚๐Ÿฌญ๐œดงโŽป๐œท—๐œด—๐œดฆ๐œตŽโŽป๐œดง๐Ÿฌญโ–‚ + โ–โ–‚๐œดงโ”๐œตผ๐œตถ๐œถฎโ–‚๐Ÿฌญ๐Ÿฌญ๐œด๐Ÿญ— ๐Ÿฌ๐œดœ๐Ÿฌญ๐Ÿฌญโ–‚๐œถฎโ–๐œตโ”๐œดงโ–‚โ– + ๐œท“๐œต๐Ÿฌ‚๐Ÿฌ‚๐Ÿฎ‚๐œถฎ๐œดธ๐œดชโ”๐œด†๐Ÿฎ‚โ–” โ–”๐Ÿฎ‚๐œด†โ”๐œดฉ๐œตณ๐œดธ๐Ÿฎ‚๐Ÿฌ‚๐Ÿฌ‚๐œถš๐Ÿฌฟ + ๐Ÿญ„๐Ÿฌจโ– ๐Ÿฌœ๐Ÿฎ‚โ–” โ–โ–‚โ–‚๐Ÿฌญ๐Ÿฌญโ–‚โ–‚โ– โ–”๐Ÿฎ‚๐Ÿฌช ๐Ÿฎˆ๐Ÿฌ•๐œถฟ + ๐Ÿญ„๐Ÿญ™โ–โ–Ž๐œตซ ๐Ÿญƒโ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–ˆ๐ŸญŽ ๐Ÿญข๐œถก๐Ÿฎ‡โ–Œ๐Ÿญค๐œถฟ + ๐Ÿญ„๐Ÿญ™ โ–๐Ÿญ„๐Ÿญ™ ๐Ÿญ‹โ–ˆโ–ˆโ–ˆโ–ˆโ–ˆ๐Ÿญœ๐œดฆโ–ˆโ–ˆโ–ˆโ–ˆโ–ˆ๐Ÿญ€ ๐Ÿญค๐Ÿญโ–Œ ๐Ÿญค๐Ÿญ + ๐œตซ๐Ÿญ— ๐Ÿฎ‰๐Ÿญ› ๐Ÿญƒโ–ˆโ–ˆโ–ˆ๐Ÿญ๐Ÿญ™ ๐Ÿญฅ๐Ÿญ’โ–ˆโ–ˆโ–ˆ๐ŸญŽ ๐Ÿญฆโ–‹ ๐Ÿญข๐œถก +โ–Ÿ๐Ÿฌฎ๐œดงโ”๐Ÿญท๐Ÿญ˜ ๐Ÿญ‹โ–ˆโ–ˆโ–ˆโ–ˆโ–„โ–‚ โ–‚โ–„โ–ˆโ–ˆโ–ˆโ–ˆ๐Ÿญ€ ๐Ÿญฃ๐œด‡โ”๐œดง๐Ÿฌฏ๐Ÿญ +๐Ÿญ•๐œดธ๐œด†โ•พ๐Ÿญบ๐Ÿฌฝ ๐Ÿญƒโ–ˆโ–ˆ๐Ÿญก ๐Ÿญ–โ–ˆโ–ˆ๐Ÿญ ๐Ÿญˆ๐œตกโ•ผ๐œด†๐Ÿฌก๐Ÿญ  + ๐œดค๐Ÿฌผ ๐Ÿฎ‰๐Ÿญ€ ๐Ÿญ‹โ–ˆโ–ˆโ–ˆฮ„ ๐Ÿญคโ–ˆโ–ˆโ–ˆ๐Ÿญ€ ๐Ÿญ‹โ–Š ๐Ÿญ‡๐œต + ๐œด ๐Ÿฌพ โ–๐œดข๐Ÿฌพโ–โ– ๐Ÿญƒโ–ˆโ–ˆ๐œด ๐œถ˜โ–ˆโ–ˆ๐Ÿญ โ–โ–๐Ÿญ‰๐Ÿญซโ–Œ ๐Ÿญ‰๐Ÿญ  + ๐œด ๐Ÿฌพ๐Ÿฎˆโ–Ž๐œถš โ–”๐Ÿฎ‚๐Ÿฌ‚โ–€๐ŸฌŽ๐ŸฌŽ ๐Ÿฎ…๐ŸฌŽโ–€๐Ÿฌ‚๐Ÿฎ‚โ–” ๐œต๐Ÿฎ‡โ–Œ๐Ÿญ‰๐œด + ๐œดข๐Ÿญฟโ– ๐œดฌโ–‚โ– โ–โ–‚๐Ÿฌœฮ„๐Ÿฎˆ๐Ÿฌฒ๐œด + ๐Ÿญฅ๐œถก๐Ÿฌญ๐Ÿฌญโ–‚๐œถญโ–๐œตโ”๐œดงโ–‚โ– โ–โ–‚๐œดงโ”๐œถท๐œถญ๐œถฎโ–‚๐Ÿฌญ๐Ÿฌญ๐œตซ๐Ÿญš + โ–”โŽบ๐œด†โ”๐œดฉโ–”๐œถฎ๐Ÿฎ‚๐Ÿฌ‚๐Ÿฌ‚๐œดœ๐Ÿฌผ ๐Ÿญ‡๐œด๐Ÿฌ‚๐Ÿฌ‚๐Ÿฎ‚๐œถฎโ–”๐œดจโ”๐œด†โŽบโ–” + โ ˆ๐Ÿฎ‚๐Ÿญท๐œด†โ•พ๐œถฆ๐œทž๐Ÿญ„๐œดฌโ•ผ๐œด†๐Ÿฌ‚๐Ÿฎ‚ฮ„ \ No newline at end of file diff --git a/src/logo/ascii/minimal.txt b/src/logo/ascii/minimal.txt new file mode 100644 index 0000000000..7d74c9950b --- /dev/null +++ b/src/logo/ascii/minimal.txt @@ -0,0 +1,18 @@ + ##### + ##### + ##### + ###### + ###### +##### +##### ##### + ###### ##### + ###### ##### + ###### ###### + ##### ###### + ##### ##### + ##### + ###### + ###### + ###### + ##### + ##### diff --git a/src/logo/ascii/ada.txt b/src/logo/ascii/xray_os.txt similarity index 100% rename from src/logo/ascii/ada.txt rename to src/logo/ascii/xray_os.txt diff --git a/src/logo/builtin.c b/src/logo/builtin.c index 76d123a698..3ea22dfd46 100644 --- a/src/logo/builtin.c +++ b/src/logo/builtin.c @@ -11,16 +11,6 @@ const FFlogo ffLogoUnknown = { }; static const FFlogo A[] = { - //Ada - { - .names = {"Ada"}, - .lines = FASTFETCH_DATATEXT_LOGO_ADA, - .colors = { - FF_COLOR_FG_256 "15", - FF_COLOR_FG_256 "14", - FF_COLOR_FG_256 "16", - } - }, // Adรฉlie { .names = {"Adรฉlie", "Adelie"}, @@ -701,6 +691,15 @@ static const FFlogo A[] = { FF_COLOR_FG_WHITE, }, }, + // AxOS + { + .names = {"AxOS"}, + .lines = FASTFETCH_DATATEXT_LOGO_AXOS, + .colors = { + FF_COLOR_FG_RGB "222;6;255", + FF_COLOR_FG_RGB "222;6;255", + }, + }, // Azos { .names = {"Azos"}, @@ -1771,8 +1770,7 @@ static const FFlogo F[] = { }, // FedoraSilverblue { - .names = {"Fedora_silverblue", "fedora-silverblue"}, - .type = FF_LOGO_LINE_TYPE_ALTER_BIT, + .names = {"Fedora-Silverblue"}, .lines = FASTFETCH_DATATEXT_LOGO_FEDORA_SILVERBLUE, .colors = { FF_COLOR_FG_BLUE, @@ -1784,8 +1782,7 @@ static const FFlogo F[] = { }, // FedoraKinoite { - .names = {"Fedora_kinoite", "fedora-kinoite"}, - .type = FF_LOGO_LINE_TYPE_ALTER_BIT, + .names = {"Fedora-Kinoite"}, .lines = FASTFETCH_DATATEXT_LOGO_FEDORA_KINOITE, .colors = { FF_COLOR_FG_BLUE, @@ -1796,8 +1793,7 @@ static const FFlogo F[] = { }, // FedoraSericea { - .names = {"Fedora_sericea", "fedora-sericea"}, - .type = FF_LOGO_LINE_TYPE_ALTER_BIT, + .names = {"Fedora-Sericea"}, .lines = FASTFETCH_DATATEXT_LOGO_FEDORA_SERICEA, .colors = { FF_COLOR_FG_BLUE, @@ -1808,8 +1804,7 @@ static const FFlogo F[] = { }, // FedoraCoreOS { - .names = {"Fedora_coreos", "fedora-coreos"}, - .type = FF_LOGO_LINE_TYPE_ALTER_BIT, + .names = {"Fedora-CoreOS"}, .lines = FASTFETCH_DATATEXT_LOGO_FEDORA_COREOS, .colors = { FF_COLOR_FG_BLUE, @@ -3109,6 +3104,16 @@ static const FFlogo M[] = { .colorKeys = FF_COLOR_FG_LIGHT_BLACK, .colorTitle = FF_COLOR_FG_DEFAULT, }, + // Minimal System + { + .names = {"Minimal_System"}, + .lines = FASTFETCH_DATATEXT_LOGO_MINIMAL, + .colors = { + FF_COLOR_FG_RED, + }, + .colorKeys = FF_COLOR_FG_CYAN, + .colorTitle = FF_COLOR_FG_DEFAULT, + }, // Minix { .names = {"Minix"}, @@ -5391,6 +5396,16 @@ static const FFlogo X[] = { FF_COLOR_FG_DEFAULT, }, }, + //Xray_OS + { + .names = {"Xray_OS"}, + .lines = FASTFETCH_DATATEXT_LOGO_XRAY_OS, + .colors = { + FF_COLOR_FG_256 "15", + FF_COLOR_FG_256 "14", + FF_COLOR_FG_256 "16", + } + }, // LAST {}, }; diff --git a/src/logo/logo.c b/src/logo/logo.c index abf8be9e24..0052ac7f8f 100644 --- a/src/logo/logo.c +++ b/src/logo/logo.c @@ -436,6 +436,13 @@ static void logoPrintNone(void) static bool logoPrintBuiltinIfExists(const FFstrbuf* name, FFLogoSize size) { + if(name->chars[0] == '~' || name->chars[0] == '.' || name->chars[0] == '/' + #if _WIN32 + || (ffCharIsEnglishAlphabet(name->chars[0]) && name->chars[1] == ':') // Windows drive letter + #endif + ) + return false; // Paths + if(ffStrbufIgnCaseEqualS(name, "none")) { logoPrintNone(); @@ -466,21 +473,22 @@ static bool logoPrintData(bool doColorReplacement, FFstrbuf* source) return true; } -static void updateLogoPath(void) +static bool updateLogoPath(void) { FFOptionsLogo* options = &instance.config.logo; if(ffPathExists(options->source.chars, FF_PATHTYPE_FILE)) - return; + return true; if (ffStrbufEqualS(&options->source, "-")) // stdin - return; + return true; FF_STRBUF_AUTO_DESTROY fullPath = ffStrbufCreate(); if (ffPathExpandEnv(options->source.chars, &fullPath) && ffPathExists(fullPath.chars, FF_PATHTYPE_FILE)) { - ffStrbufSet(&options->source, &fullPath); - return; + ffStrbufDestroy(&options->source); + ffStrbufInitMove(&options->source, &fullPath); + return true; } FF_LIST_FOR_EACH(FFstrbuf, dataDir, instance.state.platform.dataDirs) @@ -492,10 +500,13 @@ static void updateLogoPath(void) if(ffPathExists(fullPath.chars, FF_PATHTYPE_FILE)) { - ffStrbufSet(&options->source, &fullPath); - break; + ffStrbufDestroy(&options->source); + ffStrbufInitMove(&options->source, &fullPath); + return true; } } + + return false; } static bool logoPrintFileIfExists(bool doColorReplacement, bool raw) @@ -579,7 +590,13 @@ static bool logoTryKnownType(void) return logoPrintData(false, &source); } - updateLogoPath(); //We sure have a file, resolve relative paths + //We sure have a file, resolve relative paths + if (!updateLogoPath()) + { + if (instance.config.display.showErrors) + fprintf(stderr, "Logo: Failed to resolve logo source: %s\n", options->source.chars); + return false; + } if(options->type == FF_LOGO_TYPE_FILE) return logoPrintFileIfExists(true, false); @@ -642,31 +659,46 @@ void ffLogoPrint(void) return; //Make sure the logo path is set correctly. - updateLogoPath(); - - const FFTerminalResult* terminal = ffDetectTerminal(); - - //Terminal emulators that support kitty graphics protocol. - bool supportsKitty = - ffStrbufIgnCaseEqualS(&terminal->processName, "kitty") || - ffStrbufIgnCaseEqualS(&terminal->processName, "konsole") || - ffStrbufIgnCaseEqualS(&terminal->processName, "wezterm") || - ffStrbufIgnCaseEqualS(&terminal->processName, "wayst") || - ffStrbufIgnCaseEqualS(&terminal->processName, "ghostty") || - #ifdef __APPLE__ - ffStrbufIgnCaseEqualS(&terminal->processName, "WarpTerminal") || - #else - ffStrbufIgnCaseEqualS(&terminal->processName, "warp") || - #endif - false; + if (updateLogoPath()) + { + if (ffStrbufEndsWithIgnCaseS(&options->source, ".raw")) + { + if(logoPrintFileIfExists(false, true)) + return; + } - //Try to load the logo as an image. If it succeeds, print it and return. - if(logoPrintImageIfExists(supportsKitty ? FF_LOGO_TYPE_IMAGE_KITTY : FF_LOGO_TYPE_IMAGE_CHAFA, false)) - return; + if (!ffStrbufEndsWithIgnCaseS(&options->source, ".txt")) + { + const FFTerminalResult* terminal = ffDetectTerminal(); + + //Terminal emulators that support kitty graphics protocol. + bool supportsKitty = + ffStrbufIgnCaseEqualS(&terminal->processName, "kitty") || + ffStrbufIgnCaseEqualS(&terminal->processName, "konsole") || + ffStrbufIgnCaseEqualS(&terminal->processName, "wezterm") || + ffStrbufIgnCaseEqualS(&terminal->processName, "wayst") || + ffStrbufIgnCaseEqualS(&terminal->processName, "ghostty") || + #ifdef __APPLE__ + ffStrbufIgnCaseEqualS(&terminal->processName, "WarpTerminal") || + #else + ffStrbufIgnCaseEqualS(&terminal->processName, "warp") || + #endif + false; + + //Try to load the logo as an image. If it succeeds, print it and return. + if(logoPrintImageIfExists(supportsKitty ? FF_LOGO_TYPE_IMAGE_KITTY : FF_LOGO_TYPE_IMAGE_CHAFA, false)) + return; + } - //Try to load the logo as a file. If it succeeds, print it and return. - if(logoPrintFileIfExists(true, false)) - return; + //Try to load the logo as a file. If it succeeds, print it and return. + if(logoPrintFileIfExists(true, false)) + return; + } + else + { + if (instance.config.display.showErrors) + fprintf(stderr, "Logo: Failed to resolve logo source: %s\n", options->source.chars); + } logoPrintDetected(FF_LOGO_SIZE_UNKNOWN); } diff --git a/src/modules/disk/disk.c b/src/modules/disk/disk.c index af579f93f9..a2fe912f3b 100644 --- a/src/modules/disk/disk.c +++ b/src/modules/disk/disk.c @@ -125,7 +125,8 @@ static void printDisk(FFDiskOptions* options, const FFDisk* disk, uint32_t index bool isHidden = !!(disk->type & FF_DISK_VOLUME_TYPE_HIDDEN_BIT); bool isReadOnly = !!(disk->type & FF_DISK_VOLUME_TYPE_READONLY_BIT); - uint64_t duration = ffTimeGetNow() - disk->createTime; + uint64_t now = ffTimeGetNow(); + uint64_t duration = now - disk->createTime; uint32_t milliseconds = (uint32_t) (duration % 1000); duration /= 1000; uint32_t seconds = (uint32_t) (duration % 60); @@ -136,6 +137,7 @@ static void printDisk(FFDiskOptions* options, const FFDisk* disk, uint32_t index duration /= 24; uint32_t days = (uint32_t) duration; + FFTimeGetAgeResult age = ffTimeGetAge(disk->createTime, now); FF_PRINT_FORMAT_CHECKED(key.chars, 0, &options->moduleArgs, FF_PRINT_TYPE_NO_CUSTOM_KEY, ((FFformatarg[]) { FF_FORMAT_ARG(usedPretty, "size-used"), FF_FORMAT_ARG(totalPretty, "size-total"), @@ -158,6 +160,9 @@ static void printDisk(FFDiskOptions* options, const FFDisk* disk, uint32_t index FF_FORMAT_ARG(milliseconds, "milliseconds"), FF_FORMAT_ARG(disk->mountpoint, "mountpoint"), FF_FORMAT_ARG(disk->mountFrom, "mount-from"), + FF_FORMAT_ARG(age.years, "years"), + FF_FORMAT_ARG(age.daysOfYear, "days-of-year"), + FF_FORMAT_ARG(age.yearsFraction, "years-fraction"), })); } } @@ -523,6 +528,9 @@ static FFModuleBaseInfo ffModuleInfo = { {"Milliseconds after creation", "milliseconds"}, {"Mount point / drive letter", "mountpoint"}, {"Mount from (device path)", "mount-from"}, + {"Years integer after creation", "years"}, + {"Days of year after creation", "days-of-year"}, + {"Years fraction after creation", "years-fraction"}, })) }; diff --git a/src/modules/separator/separator.c b/src/modules/separator/separator.c index 4a61184780..8f9b8f4e5c 100644 --- a/src/modules/separator/separator.c +++ b/src/modules/separator/separator.c @@ -188,11 +188,6 @@ static FFModuleBaseInfo ffModuleInfo = { .parseJsonObject = (void*) ffParseSeparatorJsonObject, .printModule = (void*) ffPrintSeparator, .generateJsonConfig = (void*) ffGenerateSeparatorJsonConfig, - .formatArgs = FF_FORMAT_ARG_LIST(((FFModuleFormatArg[]) { - {"Separator string", "string"}, - {"Output color", "outputColor"}, - {"Length", "length"}, - })) }; void ffInitSeparatorOptions(FFSeparatorOptions* options) diff --git a/src/modules/uptime/uptime.c b/src/modules/uptime/uptime.c index 6c3a3d2c26..64873cc19c 100644 --- a/src/modules/uptime/uptime.c +++ b/src/modules/uptime/uptime.c @@ -39,6 +39,8 @@ void ffPrintUptime(FFUptimeOptions* options) uptime /= 24; uint32_t days = (uint32_t) uptime; + FFTimeGetAgeResult age = ffTimeGetAge(result.bootTime, ffTimeGetNow()); + FF_PRINT_FORMAT_CHECKED(FF_UPTIME_MODULE_NAME, 0, &options->moduleArgs, FF_PRINT_TYPE_DEFAULT, ((FFformatarg[]){ FF_FORMAT_ARG(days, "days"), FF_FORMAT_ARG(hours, "hours"), @@ -46,6 +48,9 @@ void ffPrintUptime(FFUptimeOptions* options) FF_FORMAT_ARG(seconds, "seconds"), FF_FORMAT_ARG(milliseconds, "milliseconds"), {FF_FORMAT_ARG_TYPE_STRING, ffTimeToShortStr(result.bootTime), "boot-time"}, + FF_FORMAT_ARG(age.years, "years"), + FF_FORMAT_ARG(age.daysOfYear, "days-of-year"), + FF_FORMAT_ARG(age.yearsFraction, "years-fraction"), })); } } @@ -110,12 +115,15 @@ static FFModuleBaseInfo ffModuleInfo = { .generateJsonResult = (void*) ffGenerateUptimeJsonResult, .generateJsonConfig = (void*) ffGenerateUptimeJsonConfig, .formatArgs = FF_FORMAT_ARG_LIST(((FFModuleFormatArg[]) { - {"Days", "days"}, - {"Hours", "hours"}, - {"Minutes", "minutes"}, - {"Seconds", "seconds"}, - {"Milliseconds", "milliseconds"}, + {"Days after boot", "days"}, + {"Hours after boot", "hours"}, + {"Minutes after boot", "minutes"}, + {"Seconds after boot", "seconds"}, + {"Milliseconds after boot", "milliseconds"}, {"Boot time in local timezone", "boot-time"}, + {"Years integer after boot", "years"}, + {"Days of year after boot", "days-of-year"}, + {"Years fraction after boot", "years-fraction"}, })) }; diff --git a/src/modules/users/users.c b/src/modules/users/users.c index e50b17ced8..74a608eb2f 100644 --- a/src/modules/users/users.c +++ b/src/modules/users/users.c @@ -78,6 +78,8 @@ void ffPrintUsers(FFUsersOptions* options) duration /= 24; uint32_t days = (uint32_t) duration; + FFTimeGetAgeResult age = ffTimeGetAge(user->loginTime, ffTimeGetNow()); + FF_PRINT_FORMAT_CHECKED(FF_USERS_MODULE_NAME, users.length == 1 ? 0 : (uint8_t) (i + 1), &options->moduleArgs, FF_PRINT_TYPE_DEFAULT, ((FFformatarg[]){ FF_FORMAT_ARG(user->name, "name"), FF_FORMAT_ARG(user->hostName, "host-name"), @@ -89,6 +91,9 @@ void ffPrintUsers(FFUsersOptions* options) FF_FORMAT_ARG(minutes, "minutes"), FF_FORMAT_ARG(seconds, "seconds"), FF_FORMAT_ARG(milliseconds, "milliseconds"), + FF_FORMAT_ARG(age.years, "years"), + FF_FORMAT_ARG(age.daysOfYear, "days-of-year"), + FF_FORMAT_ARG(age.yearsFraction, "years-fraction"), })); } } @@ -222,6 +227,9 @@ static FFModuleBaseInfo ffModuleInfo = { {"Minutes after login", "minutes"}, {"Seconds after login", "seconds"}, {"Milliseconds after login", "milliseconds"}, + {"Years integer after login", "years"}, + {"Days of year after login", "days-of-year"}, + {"Years fraction after login", "years-fraction"}, })) }; diff --git a/src/options/display.c b/src/options/display.c index 61393a91ba..cc4b703e65 100644 --- a/src/options/display.c +++ b/src/options/display.c @@ -221,6 +221,23 @@ const char* ffOptionsParseDisplayJsonConfig(FFOptionsDisplay* options, yyjson_va else return "display.bar must be an object"; } + else if (ffStrEqualsIgnCase(key, "fraction")) + { + if (yyjson_is_obj(val)) + { + yyjson_val* ndigits = yyjson_obj_get(val, "ndigits"); + if (ndigits) + { + if (yyjson_is_null(ndigits)) + options->fractionNdigits = -1; + else if (!yyjson_is_int(ndigits)) + return "display.fraction.ndigits must be an integer"; + options->fractionNdigits = (int8_t) yyjson_get_int(ndigits); + } + } + else + return "display.fraction must be an object"; + } else if (ffStrEqualsIgnCase(key, "noBuffer")) options->noBuffer = yyjson_get_bool(val); else if (ffStrEqualsIgnCase(key, "keyWidth")) @@ -462,6 +479,8 @@ bool ffOptionsParseDisplayCommandLine(FFOptionsDisplay* options, const char* key else return false; } + else if(ffStrEqualsIgnCase(key, "--fraction-ndigits")) + options->fractionNdigits = (int8_t) ffOptionParseInt32(key, value); else if(ffStrEqualsIgnCase(key, "--no-buffer")) options->noBuffer = ffOptionParseBoolean(value); else if(ffStrStartsWithIgnCase(key, "--bar-")) @@ -539,6 +558,7 @@ void ffOptionsInitDisplay(FFOptionsDisplay* options) ffStrbufInitStatic(&options->percentColorYellow, instance.state.terminalLightTheme ? FF_COLOR_FG_YELLOW : FF_COLOR_FG_LIGHT_YELLOW); ffStrbufInitStatic(&options->percentColorRed, instance.state.terminalLightTheme ? FF_COLOR_FG_RED : FF_COLOR_FG_LIGHT_RED); options->freqNdigits = 2; + options->fractionNdigits = -1; ffListInit(&options->constants, sizeof(FFstrbuf)); } diff --git a/src/options/display.h b/src/options/display.h index 74c4ecb3bb..d531dd7bc4 100644 --- a/src/options/display.h +++ b/src/options/display.h @@ -60,6 +60,7 @@ typedef struct FFOptionsDisplay uint16_t keyWidth; uint16_t keyPaddingLeft; int8_t freqNdigits; + int8_t fractionNdigits; FFlist constants; // list of FFstrbuf } FFOptionsDisplay; diff --git a/src/util/FFstrbuf.c b/src/util/FFstrbuf.c index 3fcebbd2ee..943d0de82e 100644 --- a/src/util/FFstrbuf.c +++ b/src/util/FFstrbuf.c @@ -272,6 +272,31 @@ void ffStrbufTrimRight(FFstrbuf* strbuf, char c) strbuf->chars[strbuf->length] = '\0'; } +void ffStrbufTrimLeftSpace(FFstrbuf* strbuf) +{ + if(strbuf->length == 0) + return; + + uint32_t index = 0; + while(index < strbuf->length && isspace(strbuf->chars[index])) + ++index; + + if(index == 0) + return; + + if(strbuf->allocated == 0) + { + //static string + strbuf->length -= index; + strbuf->chars += index; + return; + } + + memmove(strbuf->chars, strbuf->chars + index, strbuf->length - index); + strbuf->length -= index; + strbuf->chars[strbuf->length] = '\0'; +} + void ffStrbufTrimRightSpace(FFstrbuf* strbuf) { if (strbuf->length == 0) diff --git a/src/util/FFstrbuf.h b/src/util/FFstrbuf.h index 841a4634d3..d7fdab3547 100644 --- a/src/util/FFstrbuf.h +++ b/src/util/FFstrbuf.h @@ -53,6 +53,7 @@ FF_C_PRINTF(2, 3) void ffStrbufSetF(FFstrbuf* strbuf, const char* format, ...); void ffStrbufTrimLeft(FFstrbuf* strbuf, char c); void ffStrbufTrimRight(FFstrbuf* strbuf, char c); +void ffStrbufTrimLeftSpace(FFstrbuf* strbuf); void ffStrbufTrimRightSpace(FFstrbuf* strbuf); bool ffStrbufRemoveSubstr(FFstrbuf* strbuf, uint32_t startIndex, uint32_t endIndex); @@ -473,7 +474,7 @@ static inline FF_C_NODISCARD bool ffStrbufEndsWithS(const FFstrbuf* strbuf, cons return ffStrbufEndsWithNS(strbuf, (uint32_t) strlen(end), end); } -static inline FF_C_NODISCARD bool ffStrbufEndsWithFn(const FFstrbuf* strbuf, int (*fn)(int)) +static inline FF_C_NODISCARD bool ffStrbufEndsWithFn(const FFstrbuf* strbuf, int (*const fn)(int)) { return strbuf->length == 0 ? false : fn(strbuf->chars[strbuf->length - 1]); @@ -507,6 +508,12 @@ static inline void ffStrbufTrim(FFstrbuf* strbuf, char c) ffStrbufTrimLeft(strbuf, c); } +static inline void ffStrbufTrimSpace(FFstrbuf* strbuf) +{ + ffStrbufTrimRightSpace(strbuf); + ffStrbufTrimLeftSpace(strbuf); +} + static inline bool ffStrbufMatchSeparatedS(const FFstrbuf* strbuf, const char* comp, char separator) { return ffStrbufMatchSeparatedNS(strbuf, (uint32_t) strlen(comp), comp, separator); diff --git a/src/util/platform/FFPlatform_windows.c b/src/util/platform/FFPlatform_windows.c index b78aa8cc71..92f4aad6a0 100644 --- a/src/util/platform/FFPlatform_windows.c +++ b/src/util/platform/FFPlatform_windows.c @@ -170,13 +170,13 @@ static void getUserShell(FFPlatform* platform) static void detectWine(FFstrbuf* buf) { - static const char *(__cdecl *pwine_get_version)(void); + const char * __cdecl wine_get_version(void); HMODULE hntdll = GetModuleHandleW(L"ntdll.dll"); if (!hntdll) return; - pwine_get_version = (void *)GetProcAddress(hntdll, "wine_get_version"); - if (!pwine_get_version) return; + FF_LIBRARY_LOAD_SYMBOL_LAZY(hntdll, wine_get_version); + if (!ffwine_get_version) return; ffStrbufAppendS(buf, buf->length ? " - wine " : "wine "); - ffStrbufAppendS(buf, pwine_get_version()); + ffStrbufAppendS(buf, ffwine_get_version()); } static void getSystemReleaseAndVersion(FFPlatformSysinfo* info) diff --git a/src/util/windows/nt.h b/src/util/windows/nt.h index 53b33169be..a33870796a 100644 --- a/src/util/windows/nt.h +++ b/src/util/windows/nt.h @@ -3,6 +3,8 @@ #include #include +#define D3DKMT_ALIGN64 __attribute__((aligned(8))) + typedef struct _PROCESSOR_POWER_INFORMATION { ULONG Number; ULONG MaxMhz; @@ -23,3 +25,210 @@ NTSTATUS NTAPI NtPowerInformation( NTSTATUS NTAPI RtlGetVersion( _Inout_ PRTL_OSVERSIONINFOW lpVersionInformation ); + +#if __has_include() +#include +#else +typedef UINT D3DKMT_HANDLE; + +typedef struct _D3DKMT_OPENADAPTERFROMLUID +{ + LUID AdapterLuid; + D3DKMT_HANDLE hAdapter; +} D3DKMT_OPENADAPTERFROMLUID; +EXTERN_C _Check_return_ NTSTATUS APIENTRY D3DKMTOpenAdapterFromLuid(_Inout_ CONST D3DKMT_OPENADAPTERFROMLUID*); + +typedef struct _D3DKMT_CLOSEADAPTER +{ + D3DKMT_HANDLE hAdapter; // in: adapter handle +} D3DKMT_CLOSEADAPTER; +EXTERN_C _Check_return_ NTSTATUS APIENTRY D3DKMTCloseAdapter(_In_ CONST D3DKMT_CLOSEADAPTER*); + +typedef struct _D3DKMT_ADAPTERTYPE +{ + union + { + struct + { + UINT RenderSupported : 1; // WDDM 1.2, Windows 8 + UINT DisplaySupported : 1; + UINT SoftwareDevice : 1; + UINT PostDevice : 1; + UINT HybridDiscrete : 1; // WDDM 1.3, Windows 8.1 + UINT HybridIntegrated : 1; + UINT IndirectDisplayDevice : 1; + UINT Paravirtualized : 1; // WDDM 2.3, Windows 10 Fall Creators Update (version 1709) + UINT ACGSupported : 1; + UINT SupportSetTimingsFromVidPn : 1; + UINT Detachable : 1; + UINT ComputeOnly : 1; // WDDM 2.6, Windows 10 May 2019 Update (Version 1903) + UINT Prototype : 1; + UINT RuntimePowerManagement : 1; // WDDM 2.9, Windows 10 Insider Preview "Iron" + UINT Reserved : 18; + }; + UINT Value; + }; +} D3DKMT_ADAPTERTYPE; + +typedef enum _KMTQUERYADAPTERINFOTYPE +{ + KMTQAITYPE_ADAPTERTYPE = 15, // WDDM 1.2, Windows 8 + KMTQAITYPE_NODEMETADATA = 25, // WDDM 2.0, Windows 10 +} KMTQUERYADAPTERINFOTYPE; +typedef struct _D3DKMT_QUERYADAPTERINFO +{ + D3DKMT_HANDLE hAdapter; + KMTQUERYADAPTERINFOTYPE Type; + VOID* pPrivateDriverData; + UINT PrivateDriverDataSize; +} D3DKMT_QUERYADAPTERINFO; +EXTERN_C _Check_return_ NTSTATUS APIENTRY D3DKMTQueryAdapterInfo(_Inout_ CONST D3DKMT_QUERYADAPTERINFO*); + +typedef enum _D3DKMT_QUERYSTATISTICS_TYPE +{ + D3DKMT_QUERYSTATISTICS_PHYSICAL_ADAPTER = 10, // WDDM 2.4, Windows 10 April 2018 Update (version 1803) + D3DKMT_QUERYSTATISTICS_NODE2 = 18, // WDDM 3.1, Windows 11 2022 Update (version 22H2) +} D3DKMT_QUERYSTATISTICS_TYPE; +typedef struct _D3DKMT_QUERYSTATISTICS_QUERY_PHYSICAL_ADAPTER +{ + ULONG PhysicalAdapterIndex; +} D3DKMT_QUERYSTATISTICS_QUERY_PHYSICAL_ADAPTER; +typedef struct _D3DKMT_QUERYSTATISTICS_QUERY_NODE2 +{ + UINT16 PhysicalAdapterIndex; + UINT16 NodeOrdinal; +} D3DKMT_QUERYSTATISTICS_QUERY_NODE2; +typedef struct _D3DKMT_ADAPTER_PERFDATA +{ + UINT32 PhysicalAdapterIndex; // in: The physical adapter index, in an LDA chain + D3DKMT_ALIGN64 ULONGLONG MemoryFrequency; // out: Clock frequency of the memory in hertz + D3DKMT_ALIGN64 ULONGLONG MaxMemoryFrequency; // out: Max memory clock frequency + D3DKMT_ALIGN64 ULONGLONG MaxMemoryFrequencyOC; // out: Clock frequency of the memory while overclocked in hertz. + D3DKMT_ALIGN64 ULONGLONG MemoryBandwidth; // out: Amount of memory transferred in bytes + D3DKMT_ALIGN64 ULONGLONG PCIEBandwidth; // out: Amount of memory transferred over PCI-E in bytes + ULONG FanRPM; // out: Fan rpm + ULONG Power; // out: Power draw of the adapter in tenths of a percentage + ULONG Temperature; // out: Temperature in deci-Celsius 1 = 0.1C + UCHAR PowerStateOverride; // out: Overrides dxgkrnls power view of linked adapters. +} D3DKMT_ADAPTER_PERFDATA; +typedef struct _D3DKMT_ADAPTER_PERFDATACAPS +{ + UINT32 PhysicalAdapterIndex; // in: The physical adapter index, in an LDA chain + D3DKMT_ALIGN64 ULONGLONG MaxMemoryBandwidth; // out: Max memory bandwidth in bytes for 1 second + D3DKMT_ALIGN64 ULONGLONG MaxPCIEBandwidth; // out: Max pcie bandwidth in bytes for 1 second + ULONG MaxFanRPM; // out: Max fan rpm + ULONG TemperatureMax; // out: Max temperature before damage levels + ULONG TemperatureWarning; // out: The temperature level where throttling begins. +} D3DKMT_ADAPTER_PERFDATACAPS; + +#define DXGK_MAX_GPUVERSION_NAME_LENGTH 32 +typedef struct _D3DKMT_GPUVERSION +{ + UINT32 PhysicalAdapterIndex; // in: The physical adapter index, in an LDA chain + WCHAR BiosVersion[DXGK_MAX_GPUVERSION_NAME_LENGTH]; //out: The gpu bios version + WCHAR GpuArchitecture[DXGK_MAX_GPUVERSION_NAME_LENGTH]; //out: The gpu architectures name. +} D3DKMT_GPUVERSION; +typedef struct _D3DKMT_QUERYSTATISTICS_PHYSICAL_ADAPTER_INFORMATION +{ + D3DKMT_ADAPTER_PERFDATA AdapterPerfData; + D3DKMT_ADAPTER_PERFDATACAPS AdapterPerfDataCaps; + D3DKMT_GPUVERSION GpuVersion; +} D3DKMT_QUERYSTATISTICS_PHYSICAL_ADAPTER_INFORMATION; +typedef struct _D3DKMT_QUERYSTATISTICS_PROCESS_NODE_INFORMATION { + D3DKMT_ALIGN64 UINT64 Reserved[34]; +} D3DKMT_QUERYSTATISTICS_PROCESS_NODE_INFORMATION; +typedef struct _D3DKMT_NODE_PERFDATA +{ + UINT32 NodeOrdinal; // in: Node ordinal of the requested engine. + UINT32 PhysicalAdapterIndex; // in: The physical adapter index, in an LDA chain + D3DKMT_ALIGN64 ULONGLONG Frequency; // out: Clock frequency of the engine in hertz + D3DKMT_ALIGN64 ULONGLONG MaxFrequency; // out: Max engine clock frequency + D3DKMT_ALIGN64 ULONGLONG MaxFrequencyOC;// out: Max engine over clock frequency + ULONG Voltage; // out: Voltage of the engine in milli volts mV + ULONG VoltageMax; // out: Max voltage levels in milli volts. + ULONG VoltageMaxOC; // out: Max voltage level while overclocked in milli volts. + // WDDM 2.5 + D3DKMT_ALIGN64 ULONGLONG MaxTransitionLatency; // out: Max transition latency to change the frequency in 100 nanoseconds +} D3DKMT_NODE_PERFDATA; +typedef struct _D3DKMT_QUERYSTATISTICS_NODE_INFORMATION { + D3DKMT_QUERYSTATISTICS_PROCESS_NODE_INFORMATION GlobalInformation; //Global statistics + D3DKMT_QUERYSTATISTICS_PROCESS_NODE_INFORMATION SystemInformation; //Statistics for system thread + D3DKMT_NODE_PERFDATA NodePerfData; + UINT32 Reserved[3]; +} D3DKMT_QUERYSTATISTICS_NODE_INFORMATION; +typedef union _D3DKMT_QUERYSTATISTICS_RESULT +{ + D3DKMT_QUERYSTATISTICS_PHYSICAL_ADAPTER_INFORMATION PhysAdapterInformation; + D3DKMT_QUERYSTATISTICS_NODE_INFORMATION NodeInformation; + uint8_t Padding[776]; +} D3DKMT_QUERYSTATISTICS_RESULT; +typedef struct _D3DKMT_QUERYSTATISTICS +{ + D3DKMT_QUERYSTATISTICS_TYPE Type; // in: type of data requested + LUID AdapterLuid; // in: adapter to get export / statistics from + HANDLE* hProcess; // in: process to get statistics for, if required for this query type + D3DKMT_QUERYSTATISTICS_RESULT QueryResult; // out: requested data + + union + { + D3DKMT_QUERYSTATISTICS_QUERY_PHYSICAL_ADAPTER QueryPhysAdapter; // in: id of physical adapter to get statistics for + D3DKMT_QUERYSTATISTICS_QUERY_NODE2 QueryNode2; // in: id of node to get statistics for + }; +} D3DKMT_QUERYSTATISTICS; +static_assert(sizeof(D3DKMT_QUERYSTATISTICS) == + #if _WIN64 + 0x328 + #else + 0x320 + #endif +, "D3DKMT_QUERYSTATISTICS structure size mismatch"); +EXTERN_C _Check_return_ NTSTATUS APIENTRY D3DKMTQueryStatistics(_In_ CONST D3DKMT_QUERYSTATISTICS*); + +#define DXGK_MAX_METADATA_NAME_LENGTH 32 +typedef enum +{ + DXGK_ENGINE_TYPE_OTHER, + DXGK_ENGINE_TYPE_3D, + DXGK_ENGINE_TYPE_VIDEO_DECODE, + DXGK_ENGINE_TYPE_VIDEO_ENCODE, + DXGK_ENGINE_TYPE_VIDEO_PROCESSING, + DXGK_ENGINE_TYPE_SCENE_ASSEMBLY, + DXGK_ENGINE_TYPE_COPY, + DXGK_ENGINE_TYPE_OVERLAY, + DXGK_ENGINE_TYPE_CRYPTO, + DXGK_ENGINE_TYPE_VIDEO_CODEC, + DXGK_ENGINE_TYPE_MAX +} DXGK_ENGINE_TYPE; +typedef struct _DXGK_NODEMETADATA_FLAGS +{ + union + { + struct + { + UINT ContextSchedulingSupported : 1; // WDDM 2.2 + UINT RingBufferFenceRelease : 1; // WDDM 2.5 + UINT SupportTrackedWorkload : 1; + UINT UserModeSubmission : 1; + UINT SupportBuildTestCommandBuffer : 1; // WDDM 3.2 + UINT Reserved : 11; + UINT MaxInFlightHwQueueBuffers : 16; + }; + UINT32 Value; + }; +} DXGK_NODEMETADATA_FLAGS; +typedef struct _DXGK_NODEMETADATA +{ + DXGK_ENGINE_TYPE EngineType; + WCHAR FriendlyName[DXGK_MAX_METADATA_NAME_LENGTH]; + DXGK_NODEMETADATA_FLAGS Flags; // WDDM 2.2 + BOOLEAN GpuMmuSupported; // WDDM 2.0 ??? + BOOLEAN IoMmuSupported; +} __attribute__((packed)) DXGK_NODEMETADATA; +typedef struct _D3DKMT_NODEMETADATA +{ + _In_ UINT NodeOrdinalAndAdapterIndex; // WDDMv2: High word is physical adapter index, low word is node ordinal + _Out_ DXGK_NODEMETADATA NodeData; +} __attribute__((packed)) D3DKMT_NODEMETADATA; +static_assert(sizeof(D3DKMT_NODEMETADATA) == 0x4E, "D3DKMT_NODEMETADATA structure size mismatch"); + +#endif diff --git a/tests/strbuf.c b/tests/strbuf.c index 0b786e18de..8d2c0131c2 100644 --- a/tests/strbuf.c +++ b/tests/strbuf.c @@ -371,6 +371,22 @@ int main(void) VERIFY(strbuf.allocated > 0); ffStrbufDestroy(&strbuf); + //ffStrbufCreateStatic / TrimSpace + ffStrbufInitStatic(&strbuf, "\n TEST\n "); + ffStrbufTrimSpace(&strbuf); + VERIFY(strbuf.length == 4); + VERIFY(strbuf.allocated > 0); + VERIFY(ffStrbufEqualS(&strbuf, "TEST")); + ffStrbufDestroy(&strbuf); + + //ffStrbufCreate / TrimSpace + ffStrbufInitS(&strbuf, "\n TEST\n "); + ffStrbufTrimSpace(&strbuf); + VERIFY(strbuf.length == 4); + VERIFY(strbuf.allocated > 0); + VERIFY(ffStrbufEqualS(&strbuf, "TEST")); + ffStrbufDestroy(&strbuf); + //ffStrbufEnsureFixedLengthFree / empty buffer ffStrbufInit(&strbuf); ffStrbufEnsureFixedLengthFree(&strbuf, 10);