Compare commits

...
Author SHA1 Message Date
dependabot[bot] e3137cb8d3 CI: Bump docker/setup-qemu-action in the github-actions group
Bumps the github-actions group with 1 update: [docker/setup-qemu-action](https://github.com/docker/setup-qemu-action).


Updates `docker/setup-qemu-action` from 4.2.0 to 4.3.0
- [Release notes](https://github.com/docker/setup-qemu-action/releases)
- [Commits](https://github.com/docker/setup-qemu-action/compare/96fe6ef7f33517b61c61be40b68a1882f3264fb8...1f40c72289eff860ee54a304f1438e3cff362e0a)

---
updated-dependencies:
- dependency-name: docker/setup-qemu-action
  dependency-version: 4.3.0
  dependency-type: direct:production
  update-type: version-update:semver-minor
  dependency-group: github-actions
...

Signed-off-by: dependabot[bot] <support@github.com>
2026-09-13 18:06:54 +00:00
Carter Li cd5a2def9a DE (Linux): uses COSMIC_VERSION if available
https://github.com/pop-os/cosmic-comp/issues/2425#issuecomment-5652401962
2026-09-13 17:33:15 +08:00
Carter Li 8334035927 Logo: small optimizations 2026-09-13 12:27:17 +08:00
Carter Li 1ee8528f10 Format: makes {#title} and {#key} honors brightColor config 2026-09-13 12:26:57 +08:00
Carter Li ced49ab2a4 Chore: dont let clang-format break my code style 2026-09-13 12:05:07 +08:00
Carter Li ec8b9be354 Packages: opens SQLite DB in immutable mode
.. to avoid `SQLITE_READONLY` errors
2026-09-13 11:13:33 +08:00
李通洲 19725cd2f7 Packages (Windows): adds --source winget to improve performance of winget package detection 2026-09-13 08:39:45 +08:00
李通洲 20c155e5b4 TerminalFont (Windows): honors JSON fragment
Fixes #2573
2026-09-13 08:39:45 +08:00
dependabot[bot] 61e3f6108c CI: Bump the github-actions group with 3 updates (#2569)
Bumps the github-actions group with 3 updates: [github/codeql-action/init](https://github.com/github/codeql-action), [github/codeql-action/analyze](https://github.com/github/codeql-action) and [vmactions/solaris-vm](https://github.com/vmactions/solaris-vm).


Updates `github/codeql-action/init` from 4.37.8 to 4.37.9
- [Release notes](https://github.com/github/codeql-action/releases)
- [Changelog](https://github.com/github/codeql-action/blob/main/CHANGELOG.md)
- [Commits](https://github.com/github/codeql-action/compare/db488ddef3bf6cb639b32c2e9a7c0a7ea8271d28...cdf488f595d80d6e07e03d4674febd5ab45fa938)

Updates `github/codeql-action/analyze` from 4.37.8 to 4.37.9
- [Release notes](https://github.com/github/codeql-action/releases)
- [Changelog](https://github.com/github/codeql-action/blob/main/CHANGELOG.md)
- [Commits](https://github.com/github/codeql-action/compare/db488ddef3bf6cb639b32c2e9a7c0a7ea8271d28...cdf488f595d80d6e07e03d4674febd5ab45fa938)

Updates `vmactions/solaris-vm` from 1.3.8 to 1.3.9
- [Release notes](https://github.com/vmactions/solaris-vm/releases)
- [Commits](https://github.com/vmactions/solaris-vm/compare/315163f088b66e55bbcc45928bd224d4973b2312...96d8d976f9e67d82ec6c7e8ce9c1060731f9e21c)

---
updated-dependencies:
- dependency-name: github/codeql-action/init
  dependency-version: 4.37.9
  dependency-type: direct:production
  update-type: version-update:semver-patch
  dependency-group: github-actions
- dependency-name: github/codeql-action/analyze
  dependency-version: 4.37.9
  dependency-type: direct:production
  update-type: version-update:semver-patch
  dependency-group: github-actions
- dependency-name: vmactions/solaris-vm
  dependency-version: 1.3.9
  dependency-type: direct:production
  update-type: version-update:semver-patch
  dependency-group: github-actions
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2026-09-12 23:38:17 +08:00
李通洲 eff8514536 CI (macOS): builds Intel version with macport 2026-09-12 00:08:16 +08:00
李通洲 f37a5a2543 OpenBSD: removes kvm dep 2026-09-11 23:38:10 +08:00
Carter Li 9a9f82b59e Processes (macOS): adds a comment 2026-09-11 10:40:44 +08:00
Carter Li 29818d2dca Top (macOS): improves performance and accuracy‌ of process name 2026-09-11 10:36:16 +08:00
Carter Li 222fe7e3f9 3rdparty (yyjson): upgrades to 0.13.0 2026-09-09 13:49:56 +08:00
Stian Halseth 7d0256fb85 CPU (Linux): adds SPARC support (#2572)
/proc/cpuinfo on sparc has none of the fields the generic fallback looks
for, so the module printed "Unknown (64)". The name is in "cpu", and the
frequency in "Cpu<n>ClkTck" -- in Hz, hex on sparc64, decimal on sparc32.
2026-09-08 20:31:55 +08:00
Carter Li a70c37fdaf Logo (Builtin): removes Zerene
Distro discontinued #2567
2026-09-07 10:58:46 +08:00
27 changed files with 2140 additions and 1257 deletions

No files matched your search

+2 -2
View File
@@ -52,7 +52,7 @@ jobs:
- name: Initialize CodeQL
if: inputs.arch == 'amd64'
uses: github/codeql-action/init@db488ddef3bf6cb639b32c2e9a7c0a7ea8271d28 # v4.37.8
uses: github/codeql-action/init@cdf488f595d80d6e07e03d4674febd5ab45fa938 # v4.37.9
with:
languages: c
@@ -64,7 +64,7 @@ jobs:
- name: perform CodeQL analysis
if: inputs.arch == 'amd64'
uses: github/codeql-action/analyze@db488ddef3bf6cb639b32c2e9a7c0a7ea8271d28 # v4.37.8
uses: github/codeql-action/analyze@cdf488f595d80d6e07e03d4674febd5ab45fa938 # v4.37.9
- name: list features
run: ./fastfetch --list-features
+1 -1
View File
@@ -17,7 +17,7 @@ jobs:
uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
- name: set up QEMU for loong64
uses: docker/setup-qemu-action@96fe6ef7f33517b61c61be40b68a1882f3264fb8 # v4.2.0
uses: docker/setup-qemu-action@1f40c72289eff860ee54a304f1438e3cff362e0a # v4.3.0
- name: build in loong64 container
run: |
+19 -2
View File
@@ -26,13 +26,30 @@ jobs:
- name: uname -a
run: uname -a
- name: install required packages
- name: install required packages with MacPorts # Homebrew dropped Intel support
if: inputs.arch == 'amd64'
run: |
curl -fsSL https://github.com/macports/macports-base/releases/download/v2.12.6/MacPorts-2.12.6-15-Sequoia.pkg -o "$RUNNER_TEMP/MacPorts.pkg"
sudo installer -pkg "$RUNNER_TEMP/MacPorts.pkg" -target /
echo /opt/local/bin >> "$GITHUB_PATH"
echo /opt/local/sbin >> "$GITHUB_PATH"
export PATH=/opt/local/bin:/opt/local/sbin:$PATH
sudo port selfupdate
sudo port install vulkan-loader vulkan-headers MoltenVK ImageMagick7 chafa lua quickjs-ng jq
- name: install required packages with Homebrew
if: inputs.arch == 'aarch64'
run: |
HOMEBREW_NO_INSTALLED_DEPENDENTS_CHECK=1 brew update
HOMEBREW_NO_INSTALLED_DEPENDENTS_CHECK=1 brew install --overwrite vulkan-loader vulkan-headers molten-vk imagemagick chafa lua quickjs-ng jq
- name: configure project
run: cmake -DSET_TWEAK=Off -DBUILD_TESTS=On .
run: |
if [ "${{ inputs.arch }}" = "amd64" ]; then
export CMAKE_PREFIX_PATH=/opt/local
export PKG_CONFIG_PATH=/opt/local/lib/pkgconfig${PKG_CONFIG_PATH:+:$PKG_CONFIG_PATH}
fi
cmake -DSET_TWEAK=Off -DBUILD_TESTS=On .
- name: build project
run: cmake --build . --target package --verbose -j4
+1 -1
View File
@@ -17,7 +17,7 @@ jobs:
uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
- name: run VM
uses: vmactions/solaris-vm@315163f088b66e55bbcc45928bd224d4973b2312 # v1.3.8
uses: vmactions/solaris-vm@96d8d976f9e67d82ec6c7e8ce9c1060731f9e21c # v1.3.9
with:
usesh: true
envs: 'CMAKE_BUILD_TYPE'
+5
View File
@@ -1,3 +1,8 @@
# Unreleased
Features:
* Added CPU name and frequency detection support on SPARC. (CPU, Linux)
# 2.68.1
Changes:
+4 -4
View File
@@ -1458,10 +1458,11 @@ if(yyjson_FOUND)
# `target_link_libraries(yyjson::yyjson)` sets rpath implicitly
endif()
# Used for dlopen finding dylibs installed by homebrew
# `/opt/homebrew/lib` is not on in dlopen search path by default
# Used for dlopen finding dylibs installed by package managers
# `/opt/homebrew/lib`, `/usr/local/lib` and `/opt/local/lib` are not on the
# dlopen search path by default
if(APPLE AND BINARY_LINK_TYPE STREQUAL "dlopen")
set(CMAKE_EXE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS} -Wl,-rpath,/opt/homebrew/lib -Wl,-rpath,/usr/local/lib")
set(CMAKE_EXE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS} -Wl,-rpath,/opt/homebrew/lib -Wl,-rpath,/usr/local/lib -Wl,-rpath,/opt/local/lib")
endif()
if(ANDROID)
@@ -1930,7 +1931,6 @@ elseif(FreeBSD)
elseif(OpenBSD)
target_link_libraries(libfastfetch
PRIVATE "m"
PRIVATE "kvm"
PRIVATE "sndio"
PRIVATE "util"
)
+1 -1
View File
@@ -1,6 +1,6 @@
{
"home": "https://github.com/ibireme/yyjson",
"license": "MIT ( embed in source )",
"version": "0.12.0",
"version": "0.13.0",
"author": "ibireme"
}
+700 -285
View File
File diff suppressed because it is too large. Load diff
+911 -674
View File
File diff suppressed because it is too large. Load diff
+38 -25
View File
@@ -4,6 +4,7 @@
#include "common/strutil.h"
#include "common/io.h"
#include "common/path.h"
#include "common/mallocHelper.h"
#include <unistd.h>
#include <pwd.h>
@@ -19,7 +20,6 @@
#elif defined(__OpenBSD__)
#include <sys/sysctl.h>
#include <sys/stat.h>
#include <kvm.h>
#include "common/path.h"
#elif defined(__HAIKU__)
#include <image.h>
@@ -68,13 +68,14 @@ static void getExePath(FFPlatform* platform) {
// Current implementation uses argv[0], which can be easily spoofed.
// See #2195
size_t exePathLen = 0;
kvm_t* kd = kvm_openfiles(nullptr, nullptr, nullptr, KVM_NO_FILES, nullptr);
if (kd) {
int kpCount;
struct kinfo_proc* kp = kvm_getprocs(kd, KERN_PROC_PID, (pid_t) platform->pid, sizeof(*kp), &kpCount);
if (kp && kpCount == 1) {
char** argv = kvm_getargv(kd, kp, 0);
if (argv && argv[0]) {
{
char argvBuf[ARG_MAX];
size_t argvSize = sizeof(argvBuf);
int argvMib[] = { CTL_KERN, KERN_PROC_ARGS, (pid_t) platform->pid, KERN_PROC_ARGV };
if (sysctl(argvMib, ARRAY_SIZE(argvMib), argvBuf, &argvSize, nullptr, 0) == 0) {
// The buffer is filled with an array of char pointers followed by the strings themselves
char** argv = (char**) argvBuf;
if (argv[0] && (char*) argv[0] >= argvBuf && (char*) argv[0] < argvBuf + argvSize) {
char* arg0 = argv[0];
if (arg0[0]) {
if (strchr(arg0, '/') != nullptr) // likely a path (absolute or relative)
@@ -97,25 +98,38 @@ static void getExePath(FFPlatform* platform) {
if (exePathLen > 0) {
struct stat st;
if (stat(exePath, &st) == 0 && S_ISREG(st.st_mode)) {
int cntp;
struct kinfo_file* kf = kvm_getfiles(kd, KERN_FILE_BYPID, (pid_t) platform->pid, sizeof(*kf), &cntp);
if (kf) {
int i;
for (i = 0; i < cntp; i++) {
if (kf[i].fd_fd == KERN_FILE_TEXT) {
// KERN_FILE_TEXT is the executable file, not a shared library, and should be unique in the list.
if (st.st_dev != (dev_t) kf[i].va_fsid || st.st_ino != (ino_t) kf[i].va_fileid) {
i = -1;
// Replicate kvm_getfiles()'s live path: {CTL_KERN, KERN_FILE, KERN_FILE_BYPID, pid, esize, count}
int fileMib[6] = { CTL_KERN, KERN_FILE, KERN_FILE_BYPID, (pid_t) platform->pid, (int) sizeof(struct kinfo_file), 0 };
size_t fileSize = 0;
if (sysctl(fileMib, ARRAY_SIZE(fileMib), nullptr, &fileSize, nullptr, 0) == 0) {
fileSize += fileSize / 8; // add ~10%
FF_AUTO_FREE struct kinfo_file* kf = (struct kinfo_file*) malloc(fileSize);
if (kf) {
int rv;
do {
fileMib[5] = (int) (fileSize / sizeof(struct kinfo_file));
rv = sysctl(fileMib, ARRAY_SIZE(fileMib), kf, &fileSize, nullptr, 0);
} while (rv == -1 && errno == ENOMEM);
if (rv == 0) {
int cntp = (int) (fileSize / sizeof(struct kinfo_file));
int i;
for (i = 0; i < cntp; i++) {
if (kf[i].fd_fd == KERN_FILE_TEXT) {
// KERN_FILE_TEXT is the executable file, not a shared library, and should be unique in the list.
if (st.st_dev != (dev_t) kf[i].va_fsid || st.st_ino != (ino_t) kf[i].va_fileid) {
i = -1;
}
break;
}
}
if (i < 0) {
exePathLen = 0;
}
break;
}
// If we can't get the list of open files, we can't verify that the file is actually the executable
// Assume it is
}
if (i < 0) {
exePathLen = 0;
}
} else {
// If we can't get the list of open files, we can't verify that the file is actually the executable
// Assume it is
}
} else {
exePathLen = 0;
@@ -124,7 +138,6 @@ static void getExePath(FFPlatform* platform) {
}
}
}
kvm_close(kd);
}
#elif defined(__sun)
ssize_t exePathLen = readlink("/proc/self/path/a.out", exePath, sizeof(exePath) - 1);
+35 -2
View File
@@ -122,8 +122,9 @@ void ffOptionParseColorNoClear(const char* value, FFstrbuf* buffer) {
value += strlen(#prefix); \
continue; \
}
#define FF_APPEND_COLOR_PROP_COND(prefix, prop) \
#define FF_APPEND_COLOR_PROP_COND(prefix, prop, honorBrightColor) \
if (ffStrStartsWithIgnCase(value, #prefix)) { \
if (honorBrightColor && instance.config.display.brightColor) ffStrbufAppendS(buffer, "1;"); \
if (instance.config.display.prop.length) ffStrbufAppend(buffer, &instance.config.display.prop); \
else ffStrbufAppendS(buffer, FF_COLOR_FG_DEFAULT); \
value += strlen(#prefix); \
@@ -131,11 +132,43 @@ void ffOptionParseColorNoClear(const char* value, FFstrbuf* buffer) {
}
if (ffCharIsEnglishAlphabet(value[0])) {
// clang-format off
FF_APPEND_COLOR_CODE_COND(reset_, FF_COLOR_MODE_RESET)
else FF_APPEND_COLOR_CODE_COND(bold_, FF_COLOR_MODE_BOLD) else FF_APPEND_COLOR_CODE_COND(bright_, FF_COLOR_MODE_BOLD) else FF_APPEND_COLOR_CODE_COND(dim_, FF_COLOR_MODE_DIM) else FF_APPEND_COLOR_CODE_COND(italic_, FF_COLOR_MODE_ITALIC) else FF_APPEND_COLOR_CODE_COND(underline_, FF_COLOR_MODE_UNDERLINE) else FF_APPEND_COLOR_CODE_COND(blink_, FF_COLOR_MODE_BLINK) else FF_APPEND_COLOR_CODE_COND(inverse_, FF_COLOR_MODE_INVERSE) else FF_APPEND_COLOR_CODE_COND(hidden_, FF_COLOR_MODE_HIDDEN) else FF_APPEND_COLOR_CODE_COND(strike_, FF_COLOR_MODE_STRIKETHROUGH) else FF_APPEND_COLOR_CODE_COND(black, FF_COLOR_FG_BLACK) else FF_APPEND_COLOR_CODE_COND(red, FF_COLOR_FG_RED) else FF_APPEND_COLOR_CODE_COND(green, FF_COLOR_FG_GREEN) else FF_APPEND_COLOR_CODE_COND(yellow, FF_COLOR_FG_YELLOW) else FF_APPEND_COLOR_CODE_COND(blue, FF_COLOR_FG_BLUE) else FF_APPEND_COLOR_CODE_COND(magenta, FF_COLOR_FG_MAGENTA) else FF_APPEND_COLOR_CODE_COND(cyan, FF_COLOR_FG_CYAN) else FF_APPEND_COLOR_CODE_COND(white, FF_COLOR_FG_WHITE) else FF_APPEND_COLOR_CODE_COND(default, FF_COLOR_FG_DEFAULT) else FF_APPEND_COLOR_CODE_COND(light_black, FF_COLOR_FG_LIGHT_BLACK) else FF_APPEND_COLOR_CODE_COND(light_red, FF_COLOR_FG_LIGHT_RED) else FF_APPEND_COLOR_CODE_COND(light_green, FF_COLOR_FG_LIGHT_GREEN) else FF_APPEND_COLOR_CODE_COND(light_yellow, FF_COLOR_FG_LIGHT_YELLOW) else FF_APPEND_COLOR_CODE_COND(light_blue, FF_COLOR_FG_LIGHT_BLUE) else FF_APPEND_COLOR_CODE_COND(light_magenta, FF_COLOR_FG_LIGHT_MAGENTA) else FF_APPEND_COLOR_CODE_COND(light_cyan, FF_COLOR_FG_LIGHT_CYAN) else FF_APPEND_COLOR_CODE_COND(light_white, FF_COLOR_FG_LIGHT_WHITE) else FF_APPEND_COLOR_PROP_COND(keys, colorKeys) else FF_APPEND_COLOR_PROP_COND(title, colorTitle) else FF_APPEND_COLOR_PROP_COND(output, colorOutput) else FF_APPEND_COLOR_PROP_COND(separator, colorSeparator) else {
else FF_APPEND_COLOR_CODE_COND(bold_, FF_COLOR_MODE_BOLD)
else FF_APPEND_COLOR_CODE_COND(bright_, FF_COLOR_MODE_BOLD)
else FF_APPEND_COLOR_CODE_COND(dim_, FF_COLOR_MODE_DIM)
else FF_APPEND_COLOR_CODE_COND(italic_, FF_COLOR_MODE_ITALIC)
else FF_APPEND_COLOR_CODE_COND(underline_, FF_COLOR_MODE_UNDERLINE)
else FF_APPEND_COLOR_CODE_COND(blink_, FF_COLOR_MODE_BLINK)
else FF_APPEND_COLOR_CODE_COND(inverse_, FF_COLOR_MODE_INVERSE)
else FF_APPEND_COLOR_CODE_COND(hidden_, FF_COLOR_MODE_HIDDEN)
else FF_APPEND_COLOR_CODE_COND(strike_, FF_COLOR_MODE_STRIKETHROUGH)
else FF_APPEND_COLOR_CODE_COND(black, FF_COLOR_FG_BLACK)
else FF_APPEND_COLOR_CODE_COND(red, FF_COLOR_FG_RED)
else FF_APPEND_COLOR_CODE_COND(green, FF_COLOR_FG_GREEN)
else FF_APPEND_COLOR_CODE_COND(yellow, FF_COLOR_FG_YELLOW)
else FF_APPEND_COLOR_CODE_COND(blue, FF_COLOR_FG_BLUE)
else FF_APPEND_COLOR_CODE_COND(magenta, FF_COLOR_FG_MAGENTA)
else FF_APPEND_COLOR_CODE_COND(cyan, FF_COLOR_FG_CYAN)
else FF_APPEND_COLOR_CODE_COND(white, FF_COLOR_FG_WHITE)
else FF_APPEND_COLOR_CODE_COND(default, FF_COLOR_FG_DEFAULT)
else FF_APPEND_COLOR_CODE_COND(light_black, FF_COLOR_FG_LIGHT_BLACK)
else FF_APPEND_COLOR_CODE_COND(light_red, FF_COLOR_FG_LIGHT_RED)
else FF_APPEND_COLOR_CODE_COND(light_green, FF_COLOR_FG_LIGHT_GREEN)
else FF_APPEND_COLOR_CODE_COND(light_yellow, FF_COLOR_FG_LIGHT_YELLOW)
else FF_APPEND_COLOR_CODE_COND(light_blue, FF_COLOR_FG_LIGHT_BLUE)
else FF_APPEND_COLOR_CODE_COND(light_magenta, FF_COLOR_FG_LIGHT_MAGENTA)
else FF_APPEND_COLOR_CODE_COND(light_cyan, FF_COLOR_FG_LIGHT_CYAN)
else FF_APPEND_COLOR_CODE_COND(light_white, FF_COLOR_FG_LIGHT_WHITE)
else FF_APPEND_COLOR_PROP_COND(keys, colorKeys, true)
else FF_APPEND_COLOR_PROP_COND(title, colorTitle, true)
else FF_APPEND_COLOR_PROP_COND(output, colorOutput, false)
else FF_APPEND_COLOR_PROP_COND(separator, colorSeparator, false)
else {
fprintf(stderr, "Error: invalid color code found: %s\n", value);
exit(479);
}
// clang-format on
} else if (value[0] == '@') {
// Xterm 256 color
++value;
+16 -19
View File
@@ -28,7 +28,6 @@
#elif defined(__OpenBSD__)
#include <sys/param.h>
#include <sys/sysctl.h>
#include <kvm.h>
#elif defined(__NetBSD__)
#include <sys/types.h>
#include <sys/sysctl.h>
@@ -423,12 +422,13 @@ void ffProcessGetInfoLinux(pid_t pid, FFstrbuf* processName, FFstrbuf* exe, cons
#elif defined(__OpenBSD__)
kvm_t* kd = kvm_open(nullptr, nullptr, nullptr, KVM_NO_FILES, nullptr);
int count = 0;
const struct kinfo_proc* proc = kvm_getprocs(kd, KERN_PROC_PID, pid, sizeof(struct kinfo_proc), &count);
if (proc) {
char** argv = kvm_getargv(kd, proc, 0);
if (argv) {
char argvBuf[ARG_MAX];
size_t argvSize = sizeof(argvBuf);
int argvMib[] = { CTL_KERN, KERN_PROC_ARGS, pid, KERN_PROC_ARGV };
if (sysctl(argvMib, ARRAY_SIZE(argvMib), argvBuf, &argvSize, nullptr, 0) == 0) {
// The buffer is filled with an array of char pointers followed by the strings themselves
char** argv = (char**) argvBuf;
if (argv[0] && (char*) argv[0] >= argvBuf && (char*) argv[0] < argvBuf + argvSize) {
const char* arg0 = argv[0];
if (arg0[0] == '-') {
arg0++;
@@ -436,7 +436,6 @@ void ffProcessGetInfoLinux(pid_t pid, FFstrbuf* processName, FFstrbuf* exe, cons
ffStrbufSetS(exe, arg0);
}
}
kvm_close(kd);
#elif defined(__HAIKU__)
@@ -633,21 +632,19 @@ const char* ffProcessGetBasicInfoLinux(pid_t pid, FFstrbuf* name, pid_t* ppid, i
#elif defined(__OpenBSD__)
kvm_t* kd = kvm_open(nullptr, nullptr, nullptr, KVM_NO_FILES, nullptr);
int count = 0;
const struct kinfo_proc* proc = kvm_getprocs(kd, KERN_PROC_PID, pid, sizeof(struct kinfo_proc), &count);
if (proc) {
ffStrbufSetS(name, proc->p_comm);
struct kinfo_proc proc;
size_t size = sizeof(proc);
int mib[] = { CTL_KERN, KERN_PROC, KERN_PROC_PID, pid, (int) sizeof(struct kinfo_proc), 1 };
if (sysctl(mib, ARRAY_SIZE(mib), &proc, &size, nullptr, 0) == 0) {
ffStrbufSetS(name, proc.p_comm);
if (ppid) {
*ppid = proc->p_ppid;
*ppid = proc.p_ppid;
}
if (tty) {
*tty = (int) proc->p_tdev;
*tty = (int) proc.p_tdev;
}
}
kvm_close(kd);
if (!proc) {
return "kvm_getprocs() failed";
} else {
return "sysctl(KERN_PROC_PID) failed";
}
#elif defined(__HAIKU__)
+69 -55
View File
@@ -3,6 +3,7 @@
#include "common/library.h"
#include "common/thread.h"
#include "common/io.h"
#include "common/debug.h"
#include <string.h>
@@ -380,72 +381,85 @@ static const SQLiteData* getSQLiteData(void) {
return &data;
}
int ffSettingsGetSQLite3Int(const char* dbPath, const char* query) {
if (!ffPathExists(dbPath, FF_PATHTYPE_FILE)) {
return 0;
}
const SQLiteData* data = getSQLiteData();
if (data == nullptr) {
return 0;
}
typedef struct FFSQLite3Bundle {
const SQLiteData* data;
sqlite3* db;
if (data->ffsqlite3_open_v2(dbPath, &db, SQLITE_OPEN_READONLY | SQLITE_OPEN_NOMUTEX, nullptr) != SQLITE_OK) {
return 0;
}
sqlite3_stmt* stmt;
if (data->ffsqlite3_prepare_v2(db, query, (int) strlen(query), &stmt, nullptr) != SQLITE_OK) {
data->ffsqlite3_close(db);
} FFSQLite3Bundle;
static void destroySQLite3Bundle(FFSQLite3Bundle* bundle) {
if (!bundle->data) {
return;
}
if (bundle->stmt) {
bundle->data->ffsqlite3_finalize(bundle->stmt);
bundle->stmt = nullptr;
}
if (bundle->db) {
bundle->data->ffsqlite3_close(bundle->db);
bundle->db = nullptr;
}
}
static bool prepareSQLite3Statement(const char* dbPath, const char* query, FFSQLite3Bundle* bundle) {
if (!ffPathExists(dbPath, FF_PATHTYPE_FILE)) {
return false;
}
const SQLiteData* data = bundle->data = getSQLiteData();
if (data == nullptr) {
FF_DEBUG("Failed to load SQLite3 library");
return false;
}
// Read-only WAL databases may require a -shm or lock file during prepare, which is not
// possible when the database directory is not writable. immutable=1 avoids those files.
FF_STRBUF_AUTO_DESTROY pathImmutable = ffStrbufCreateStatic("file:");
ffStrbufAppendS(&pathImmutable, dbPath);
ffStrbufAppendS(&pathImmutable, "?immutable=1");
int ret = data->ffsqlite3_open_v2(pathImmutable.chars, &bundle->db, SQLITE_OPEN_READONLY | SQLITE_OPEN_NOMUTEX | SQLITE_OPEN_URI, nullptr);
if (ret != SQLITE_OK) {
FF_DEBUG("Failed to open SQLite3 database: %s (%d)", dbPath, ret);
return false;
}
ret = data->ffsqlite3_prepare_v2(bundle->db, query, (int) strlen(query), &bundle->stmt, nullptr);
if (ret != SQLITE_OK) {
FF_DEBUG("Failed to prepare SQLite3 statement: %s (%d)", query, ret);
return false;
}
ret = data->ffsqlite3_step(bundle->stmt);
if (ret != SQLITE_ROW) {
FF_DEBUG("Failed to step SQLite3 statement: %s (%d)", query, ret);
return false;
}
ret = data->ffsqlite3_data_count(bundle->stmt);
if (ret < 1) {
FF_DEBUG("No data returned for SQLite3 statement: %s", query);
return false;
}
return true;
}
int ffSettingsGetSQLite3Int(const char* dbPath, const char* query) {
[[gnu::cleanup(destroySQLite3Bundle)]] FFSQLite3Bundle bundle = {};
if (!prepareSQLite3Statement(dbPath, query, &bundle)) {
return 0;
}
if (data->ffsqlite3_step(stmt) != SQLITE_ROW || data->ffsqlite3_data_count(stmt) < 1) {
data->ffsqlite3_finalize(stmt);
data->ffsqlite3_close(db);
return 0;
}
int result = data->ffsqlite3_column_int(stmt, 0);
data->ffsqlite3_finalize(stmt);
data->ffsqlite3_close(db);
return result;
return bundle.data->ffsqlite3_column_int(bundle.stmt, 0);
}
bool ffSettingsGetSQLite3String(const char* dbPath, const char* query, FFstrbuf* result) {
if (!ffPathExists(dbPath, FF_PATHTYPE_FILE)) {
[[gnu::cleanup(destroySQLite3Bundle)]] FFSQLite3Bundle bundle = {};
if (!prepareSQLite3Statement(dbPath, query, &bundle)) {
return false;
}
const SQLiteData* data = getSQLiteData();
if (data == nullptr) {
return false;
}
sqlite3* db;
if (data->ffsqlite3_open_v2(dbPath, &db, SQLITE_OPEN_READONLY, nullptr) != SQLITE_OK) {
return false;
}
sqlite3_stmt* stmt;
if (data->ffsqlite3_prepare_v2(db, query, (int) strlen(query), &stmt, nullptr) != SQLITE_OK) {
data->ffsqlite3_close(db);
return false;
}
if (data->ffsqlite3_step(stmt) != SQLITE_ROW || data->ffsqlite3_data_count(stmt) < 1) {
data->ffsqlite3_finalize(stmt);
data->ffsqlite3_close(db);
return false;
}
ffStrbufSetS(result, (const char*) data->ffsqlite3_column_text(stmt, 0));
data->ffsqlite3_finalize(stmt);
data->ffsqlite3_close(db);
ffStrbufSetS(result, (const char*) bundle.data->ffsqlite3_column_text(bundle.stmt, 0));
return true;
}
+7
View File
@@ -619,6 +619,9 @@ static const char* parseCpuInfo(
(cpu->name.length == 0 && ffParsePropLine(line, "cpu :", &cpu->name)) ||
#elif __sh__
(cpu->name.length == 0 && ffParsePropLine(line, "cpu type :", &cpu->name)) ||
#elif __sparc__ || __sparc
(cpu->name.length == 0 && ffParsePropLine(line, "cpu :", &cpu->name)) ||
(cpuMHz->length == 0 && ffParsePropLine(line, "Cpu0ClkTck :", cpuMHz)) ||
#else
(cpu->name.length == 0 && ffParsePropLine(line, "model name :", &cpu->name)) ||
(cpu->name.length == 0 && ffParsePropLine(line, "model :", &cpu->name)) ||
@@ -1101,6 +1104,10 @@ static const char* detectPhysicalCores(FFCPUResult* cpu) {
if (cpu->name.length) {
ffStrbufPrependS(&cpu->name, "Machine ");
}
#elif __sparc__ || __sparc
// Cpu0ClkTck is in Hz, printed as "%016lx" by sparc64 and "%ld" by sparc32. A 32-bit userland can run
// on a 64-bit kernel, so take the base from the width of the value rather than from our own bitness.
cpu->frequencyBase = (uint32_t) (strtoull(cpuMHz.chars, nullptr, cpuMHz.length == 16 ? 16 : 10) / 1000000);
#endif
}
+7 -1
View File
@@ -184,6 +184,12 @@ static const char* getTrinity(FFstrbuf* result, [[maybe_unused]] FFDEOptions* op
}
static const char* getCosmic(FFstrbuf* result, [[maybe_unused]] FFDEOptions* options) {
const char* env = getenv("COSMIC_VERSION");
if (env) {
ffStrbufSetS(result, env);
return nullptr;
}
if (ffProcessAppendStdOut(result, (char* const[]) { "cosmic-comp", "--version", nullptr }) == nullptr) {
// cosmic-comp 0.1.0 (git commit fa88002ba41d2edec25dd7ffdee9719fbb928fc0)
ffStrbufSubstrAfterFirstC(result, ' ');
@@ -256,7 +262,7 @@ const char* ffDetectDEVersion(const FFstrbuf* deName, FFstrbuf* result, FFDEOpti
getUnity(result, options);
} else if (ffStrbufEqualS(deName, "trinity")) {
getTrinity(result, options);
} else if (ffStrbufEqualS(deName, "COSMIC")) {
} else if (ffStrbufEqualS(deName, FF_DE_PRETTY_COSMIC)) {
getCosmic(result, options);
} else if (ffStrbufEqualS(deName, FF_DE_PRETTY_ENLIGHTENMENT)) {
getEnlightenment(result, options);
@@ -17,6 +17,7 @@
#define FF_DE_PRETTY_UKUI "UKUI"
#define FF_DE_PRETTY_NEBIDE "NebiDE"
#define FF_DE_PRETTY_ENLIGHTENMENT "Enlightenment"
#define FF_DE_PRETTY_COSMIC "COSMIC"
#define FF_WM_PRETTY_KWIN "KWin"
#define FF_WM_PRETTY_MUTTER "Mutter"
+27 -16
View File
@@ -16,7 +16,6 @@
#elif __OpenBSD__
#include <sys/param.h>
#include <sys/sysctl.h>
#include <kvm.h>
#elif __sun
#include <procfs.h>
#elif __NetBSD__
@@ -76,6 +75,10 @@ static const char* parseEnv(void) {
return "Sway";
}
if (getenv("COSMIC_VERSION") != nullptr) {
return "COSMIC";
}
#if __linux__ && !__ANDROID__
if (
getenv("WAYLAND_DISPLAY") != nullptr &&
@@ -311,25 +314,33 @@ static const char* getFromProcesses(FFDisplayServerResult* result) {
}
}
#elif __OpenBSD__
kvm_t* kd = kvm_open(nullptr, nullptr, nullptr, KVM_NO_FILES, nullptr);
int count = 0;
const struct kinfo_proc* proc = kvm_getprocs(kd, KERN_PROC_UID, (int) userId, sizeof(*proc), &count);
if (proc) {
for (int i = 0; i < count; ++i) {
if (result->dePrettyName.length == 0) {
applyPrettyNameIfDE(result, proc[i].p_comm);
}
int request[] = { CTL_KERN, KERN_PROC, KERN_PROC_UID, (int) userId, (int) sizeof(struct kinfo_proc), 0 };
size_t length = 0;
if (result->wmPrettyName.length == 0) {
applyNameIfWM(result, proc[i].p_comm);
}
if (sysctl(request, ARRAY_SIZE(request), nullptr, &length, nullptr, 0) != 0) {
return "sysctl({CTL_KERN, KERN_PROC, KERN_PROC_UID}, nullptr) failed";
}
if (result->dePrettyName.length > 0 && result->wmPrettyName.length > 0) {
break;
}
FF_AUTO_FREE struct kinfo_proc* procs = (struct kinfo_proc*) malloc(length);
request[5] = (int) (length / sizeof(struct kinfo_proc)); // count must be non-zero for data fetch
if (sysctl(request, ARRAY_SIZE(request), procs, &length, nullptr, 0) != 0) {
return "sysctl({CTL_KERN, KERN_PROC, KERN_PROC_UID}, procs) failed";
}
int count = (int) (length / sizeof(struct kinfo_proc));
for (int i = 0; i < count; ++i) {
if (result->dePrettyName.length == 0) {
applyPrettyNameIfDE(result, procs[i].p_comm);
}
if (result->wmPrettyName.length == 0) {
applyNameIfWM(result, procs[i].p_comm);
}
if (result->dePrettyName.length > 0 && result->wmPrettyName.length > 0) {
break;
}
}
kvm_close(kd);
#elif __sun
FF_AUTO_CLOSE_DIR DIR* procdir = opendir("/proc");
if (procdir == nullptr) {
+20 -13
View File
@@ -18,7 +18,6 @@
#elif __OpenBSD__
#include <sys/param.h>
#include <sys/sysctl.h>
#include <kvm.h>
#elif __sun
#include <procfs.h>
#elif __NetBSD__
@@ -55,7 +54,7 @@ static const char* getSshdVersion(FFstrbuf* version) {
#ifdef FF_HAVE_ZLIB
#include "common/library.h"
#include "common/path.h"
#include <stdlib.h>
#include <zlib.h>
@@ -274,19 +273,27 @@ const char* detectByProcesses(FFLMResult* result) {
}
}
#elif __OpenBSD__
kvm_t* kd = kvm_open(nullptr, nullptr, nullptr, KVM_NO_FILES, nullptr);
int count = 0;
const struct kinfo_proc* proc = kvm_getprocs(kd, KERN_PROC_UID, 0, sizeof(*proc), &count);
if (proc) {
for (int i = 0; i < count; ++i) {
const char* lm = testLms(proc[i].p_comm);
if (lm) {
ffStrbufSetStatic(&result->service, lm);
break;
}
int request[] = { CTL_KERN, KERN_PROC, KERN_PROC_UID, 0, (int) sizeof(struct kinfo_proc), 0 };
size_t length = 0;
if (sysctl(request, ARRAY_SIZE(request), nullptr, &length, nullptr, 0) != 0) {
return "sysctl({CTL_KERN, KERN_PROC, KERN_PROC_UID}, nullptr) failed";
}
FF_AUTO_FREE struct kinfo_proc* procs = (struct kinfo_proc*) malloc(length);
request[5] = (int) (length / sizeof(struct kinfo_proc)); // count must be non-zero for data fetch
if (sysctl(request, ARRAY_SIZE(request), procs, &length, nullptr, 0) != 0) {
return "sysctl({CTL_KERN, KERN_PROC, KERN_PROC_UID}, procs) failed";
}
int count = (int) (length / sizeof(struct kinfo_proc));
for (int i = 0; i < count; ++i) {
const char* lm = testLms(procs[i].p_comm);
if (lm) {
ffStrbufSetStatic(&result->service, lm);
break;
}
}
kvm_close(kd);
#elif __sun
FF_AUTO_CLOSE_DIR DIR* procdir = opendir("/proc");
if (procdir == nullptr) {
+15
View File
@@ -157,10 +157,25 @@ static void detectPacman(FFPackagesResult* result) {
}
static void detectWinget(FFPackagesResult* result) {
// Why not read winget's own database instead of shelling out?
// `%LOCALAPPDATA%\Packages\Microsoft.DesktopAppInstaller_8wekyb3d8bbwe\LocalState\<source>\installed.db`
// is a `PackageTrackingCatalog`: it records the install / uninstall actions winget performed for that
// source, not a snapshot of what is currently installed. Uninstalling with anything but winget leaves
// the record behind forever, so counting it also counts packages that are long gone. Its schema is
// private and versioned (1.3, while the source index is 2.0), winget opens it ReadWrite while running,
// and reading it would drag in a SQLite dependency.
// The authoritative set of installed packages comes from the ARP registry and MSIX, which is exactly
// what `winget list` enumerates before correlating it against the read-only source index.
FF_STRBUF_AUTO_DESTROY buffer = ffStrbufCreate();
if (ffProcessAppendStdOut(&buffer, (char*[]) {
"winget.exe",
"list",
// Without `--source winget`, winget also lists every package installed by
// other means (ARP / MSIX), which are not winget packages at all.
// It also skips the msstore HTTP round-trips, which are the main reason
// why `winget list` is slow and its latency unpredictable.
"--source",
"winget",
"--disable-interactivity",
nullptr,
})) {
+3 -3
View File
@@ -31,10 +31,10 @@ const char* ffDetectProcesses(const FFProcessesOptions* options, FFProcessesResu
pid_t pid = proc->kp_proc.p_pid;
struct proc_taskinfo taskInfo;
if (proc_pidinfo(pid, PROC_PIDTASKINFO, 0, &taskInfo, sizeof(taskInfo)) != sizeof(taskInfo)) {
continue;
if (proc_pidinfo(pid, PROC_PIDTASKINFO, 0, &taskInfo, sizeof(taskInfo)) == sizeof(taskInfo)) {
// NOTE: This fails for system processes
result->threads += (uint32_t) taskInfo.pti_threadnum;
}
result->threads += (uint32_t) taskInfo.pti_threadnum;
}
return nullptr;
+10 -14
View File
@@ -4,25 +4,22 @@
#include <sys/param.h>
#include <sys/sysctl.h>
#include <kvm.h>
const char* ffDetectProcesses(const FFProcessesOptions* options, FFProcessesResult* result) {
kvm_t* kd = kvm_open(nullptr, nullptr, nullptr, KVM_NO_FILES, nullptr);
if (!kd) {
return "kvm_open() failed";
int request[] = { CTL_KERN, KERN_PROC, (options->countKprocs ? KERN_PROC_KTHREAD : KERN_PROC_ALL) | KERN_PROC_SHOW_THREADS, 0, (int) sizeof(struct kinfo_proc), 0 };
size_t length = 0;
if (sysctl(request, ARRAY_SIZE(request), nullptr, &length, nullptr, 0) != 0) {
return "sysctl({CTL_KERN, KERN_PROC, KERN_PROC_ALL}, nullptr) failed";
}
int count = 0;
// KERN_PROC_ALL returns all user-level processes
// KERN_PROC_KTHREAD returns all processes, including user-level processes (despite the name)
const struct kinfo_proc* procs = kvm_getprocs(kd,
(options->countKprocs ? KERN_PROC_KTHREAD : KERN_PROC_ALL) | KERN_PROC_SHOW_THREADS,
0, sizeof(struct kinfo_proc), &count);
if (!procs) {
kvm_close(kd);
return "kvm_getprocs() failed";
FF_AUTO_FREE struct kinfo_proc* procs = (struct kinfo_proc*) malloc(length);
request[5] = (int) (length / sizeof(struct kinfo_proc)); // count must be non-zero for data fetch
if (sysctl(request, ARRAY_SIZE(request), procs, &length, nullptr, 0) != 0) {
return "sysctl({CTL_KERN, KERN_PROC, KERN_PROC_ALL}, procs) failed";
}
int count = (int) (length / sizeof(struct kinfo_proc));
for (int i = 0; i < count; ++i) {
const struct kinfo_proc* proc = &procs[i];
@@ -32,6 +29,5 @@ const char* ffDetectProcesses(const FFProcessesOptions* options, FFProcessesResu
}
}
kvm_close(kd);
return nullptr;
}
+191 -44
View File
@@ -13,26 +13,90 @@
#include <windows.h>
#include <stdlib.h>
static const char* detectWTProfile(yyjson_val* profile, FFstrbuf* name, double* size) {
// Windows Terminal resolves every setting through an inheritance chain. A single property,
// such as the font face or the font size, is resolved from the most to the least important
// source:
// 4. the active profile itself (settings.json -> profiles.list)
// 3. profiles.defaults (settings.json)
// 2. a JSON fragment updating the profile with "updates"
// 1. a JSON fragment defining the profile with "guid"
// 0. not set -> the builtin default reported below
// https://github.com/microsoft/terminal/blob/main/src/cascadia/TerminalSettingsModel/IInheritable.h
// https://github.com/microsoft/terminal/blob/main/src/cascadia/TerminalSettingsModel/CascadiaSettingsSerialization.cpp
enum {
WT_FONT_PRIORITY_FRAGMENT = 1,
WT_FONT_PRIORITY_FRAGMENT_UPDATES = 2,
WT_FONT_PRIORITY_DEFAULTS = 3,
WT_FONT_PRIORITY_PROFILE = 4,
};
typedef struct FFTerminalFontWT {
FFstrbuf name;
double size;
uint8_t namePriority; // 0 if unset
uint8_t sizePriority; // 0 if unset
} FFTerminalFontWT;
static inline void wrapWTFontFree(FFTerminalFontWT* result) {
assert(result);
ffStrbufDestroy(&result->name);
}
static FFTerminalFontWT ffTerminalFontWTCreate(void) {
FFTerminalFontWT result = {
.name = ffStrbufCreate(),
.size = -1,
};
return result;
}
static void applyWTProfile(yyjson_val* profile, uint8_t priority, FFTerminalFontWT* result) {
yyjson_val* font = yyjson_obj_get(profile, "font");
if (!font) {
return "yyjson_obj_get(profile, \"font\"); failed";
}
if (!yyjson_is_obj(font)) {
return "yyjson_is_obj(font) returns false";
return;
}
if (name->length == 0) {
ffStrbufAppendJsonVal(name, yyjson_obj_get(font, "face"));
}
if (*size < 0) {
yyjson_val* psize = yyjson_obj_get(font, "size");
if (yyjson_is_num(psize)) {
*size = unsafe_yyjson_get_num(psize);
if (result->namePriority < priority) {
yyjson_val* face = yyjson_obj_get(font, "face");
if (yyjson_is_str(face)) {
ffStrbufClear(&result->name);
ffStrbufAppendJsonVal(&result->name, face);
if (result->name.length > 0) { // an empty face is treated as unset
result->namePriority = priority;
}
}
}
if (result->sizePriority < priority) {
yyjson_val* size = yyjson_obj_get(font, "size");
if (yyjson_is_num(size)) {
result->size = unsafe_yyjson_get_num(size);
result->sizePriority = priority;
}
}
}
// Finds the profile matching `wtProfileId` in a `profiles` array.
// A fragment either defines a profile with "guid" or updates an existing one with "updates",
// the latter being more important, so it is looked up first.
// Note that "guid" and "updates" may be missing: yyjson_get_str() returns nullptr then.
static yyjson_val* findWTProfileInArray(yyjson_val* profiles, const FFstrbuf* wtProfileId, bool* fromUpdates) {
if (!yyjson_is_arr(profiles)) {
return nullptr;
}
for (uint8_t pass = 0; pass < 2; ++pass) {
yyjson_val* profile;
size_t idx, max;
yyjson_arr_foreach (profiles, idx, max, profile) {
const char* id = yyjson_get_str(yyjson_obj_get(profile, pass == 0 ? "updates" : "guid"));
if (id && ffStrbufEqualS(wtProfileId, id)) {
*fromUpdates = pass == 0;
return profile;
}
}
}
return nullptr;
}
@@ -43,7 +107,7 @@ static inline void wrapYyjsonFree(yyjson_doc** doc) {
}
}
static const char* detectFromWTImpl(FFstrbuf* content, FFstrbuf* name, double* size) {
static const char* detectFromWTSettings(FFstrbuf* content, const FFstrbuf* wtProfileId, FFTerminalFontWT* result) {
[[gnu::cleanup(wrapYyjsonFree)]] yyjson_doc* doc = yyjson_read_opts(content->chars, content->length, YYJSON_READ_ALLOW_COMMENTS | YYJSON_READ_ALLOW_TRAILING_COMMAS, nullptr, nullptr);
if (!doc) {
return "Failed to parse WT JSON config file";
@@ -57,38 +121,106 @@ static const char* detectFromWTImpl(FFstrbuf* content, FFstrbuf* name, double* s
return "yyjson_obj_get(root, \"profiles\") failed";
}
FF_STRBUF_AUTO_DESTROY wtProfileId = ffStrbufCreateS(getenv("WT_PROFILE_ID"));
ffStrbufTrim(&wtProfileId, '\'');
if (wtProfileId.length > 0) {
yyjson_val* list = yyjson_obj_get(profiles, "list");
if (yyjson_is_arr(list)) {
yyjson_val* profile;
size_t idx, max;
yyjson_arr_foreach (list, idx, max, profile) {
yyjson_val* guid = yyjson_obj_get(profile, "guid");
if (ffStrbufEqualS(&wtProfileId, yyjson_get_str(guid))) {
detectWTProfile(profile, name, size);
break;
}
}
if (wtProfileId->length > 0) {
bool fromUpdates = false;
yyjson_val* profile = findWTProfileInArray(yyjson_obj_get(profiles, "list"), wtProfileId, &fromUpdates);
if (profile) {
applyWTProfile(profile, WT_FONT_PRIORITY_PROFILE, result);
}
}
yyjson_val* defaults = yyjson_obj_get(profiles, "defaults");
if (defaults) {
detectWTProfile(defaults, name, size);
applyWTProfile(defaults, WT_FONT_PRIORITY_DEFAULTS, result);
}
if (name->length == 0) {
ffStrbufSetS(name, "Cascadia Mono");
}
if (*size < 0) {
*size = 12;
}
return nullptr;
}
// Windows Terminal reads fragment files from a two level directory layout and doesn't recurse:
// <known folder>\Microsoft\Windows Terminal\Fragments\<app-name>\<file-name>.json
// https://learn.microsoft.com/en-us/windows/terminal/json-fragment-extensions#where-to-place-the-json-fragment-files
static void applyWTFragmentFile(const char* path, const FFstrbuf* wtProfileId, FFTerminalFontWT* result) {
FF_STRBUF_AUTO_DESTROY content = ffStrbufCreate();
if (!ffReadFileBuffer(path, &content)) {
return;
}
// Fragments are optional: an unreadable or malformed one must not fail font detection
[[gnu::cleanup(wrapYyjsonFree)]] yyjson_doc* doc = yyjson_read_opts(content.chars, content.length, YYJSON_READ_ALLOW_COMMENTS | YYJSON_READ_ALLOW_TRAILING_COMMAS, nullptr, nullptr);
if (!doc) {
return;
}
yyjson_val* const root = yyjson_doc_get_root(doc);
assert(root);
bool fromUpdates = false;
yyjson_val* profile = findWTProfileInArray(yyjson_obj_get(root, "profiles"), wtProfileId, &fromUpdates);
if (profile) {
applyWTProfile(profile, fromUpdates ? WT_FONT_PRIORITY_FRAGMENT_UPDATES : WT_FONT_PRIORITY_FRAGMENT, result);
}
}
static void detectWTProfileFromFragmentsIn(const FFstrbuf* fragmentDir, const FFstrbuf* wtProfileId, FFTerminalFontWT* result) {
FF_STRBUF_AUTO_DESTROY path = ffStrbufCreateCopy(fragmentDir);
const uint32_t baseLength = path.length;
ffStrbufAppendC(&path, '*');
WIN32_FIND_DATAA entry;
FF_AUTO_CLOSE_DIR HANDLE hFind = FindFirstFileA(path.chars, &entry);
if (hFind == INVALID_HANDLE_VALUE) {
return;
}
do {
if (!(entry.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) || entry.cFileName[0] == '.') {
continue;
}
ffStrbufSubstrBefore(&path, baseLength);
ffStrbufAppendS(&path, entry.cFileName);
ffStrbufAppendC(&path, '\\');
const uint32_t appLength = path.length;
ffStrbufAppendS(&path, "*.json");
WIN32_FIND_DATAA fileEntry;
FF_AUTO_CLOSE_DIR HANDLE hFile = FindFirstFileA(path.chars, &fileEntry);
if (hFile != INVALID_HANDLE_VALUE) {
do {
if (fileEntry.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) {
continue;
}
ffStrbufSubstrBefore(&path, appLength);
ffStrbufAppendS(&path, fileEntry.cFileName);
applyWTFragmentFile(path.chars, wtProfileId, result);
} while (FindNextFileA(hFile, &fileEntry));
}
ffStrbufSubstrBefore(&path, baseLength);
} while (FindNextFileA(hFind, &entry));
}
static void detectFromWTFragments(const FFstrbuf* wtProfileId, FFTerminalFontWT* result) {
// Windows Terminal merges the user scoped fragments before the machine scoped ones,
// so the user scoped fragments are more important
static const KNOWNFOLDERID* const fragmentFolderIds[] = { &FOLDERID_LocalAppData, &FOLDERID_ProgramData };
for (uint32_t i = 0; i < ARRAY_SIZE(fragmentFolderIds); ++i) {
PWSTR folderW = nullptr;
if (SUCCEEDED(SHGetKnownFolderPath(fragmentFolderIds[i], KF_FLAG_DEFAULT, nullptr, &folderW))) {
FF_STRBUF_AUTO_DESTROY fragmentDir = ffStrbufCreateWS(folderW);
CoTaskMemFree(folderW);
ffStrbufAppendS(&fragmentDir, "\\Microsoft\\Windows Terminal\\Fragments\\");
if (ffPathExists(fragmentDir.chars, FF_PATHTYPE_DIRECTORY)) {
detectWTProfileFromFragmentsIn(&fragmentDir, wtProfileId, result);
}
}
}
}
static void detectFromWindowsTerminal(const FFstrbuf* terminalExe, FFTerminalFontResult* terminalFont) {
// https://learn.microsoft.com/en-us/windows/terminal/install#settings-json-file
FF_STRBUF_AUTO_DESTROY json = ffStrbufCreate();
@@ -159,17 +291,32 @@ static void detectFromWindowsTerminal(const FFstrbuf* terminalExe, FFTerminalFon
return;
}
FF_STRBUF_AUTO_DESTROY name = ffStrbufCreate();
double size = -1;
error = detectFromWTImpl(&json, &name, &size);
FF_STRBUF_AUTO_DESTROY wtProfileId = ffStrbufCreateS(getenv("WT_PROFILE_ID"));
ffStrbufTrim(&wtProfileId, '\'');
[[gnu::cleanup(wrapWTFontFree)]] FFTerminalFontWT result = ffTerminalFontWTCreate();
error = detectFromWTSettings(&json, &wtProfileId, &result);
if (error) {
ffStrbufAppendS(&terminalFont->error, error);
} else {
char sizeStr[16];
snprintf(sizeStr, ARRAY_SIZE(sizeStr), "%g", size);
ffFontInitValues(&terminalFont->font, name.chars, sizeStr);
return;
}
// JSON fragments are only read when settings.json doesn't fully specify the font
if (wtProfileId.length > 0 && (result.name.length == 0 || result.size < 0)) {
detectFromWTFragments(&wtProfileId, &result);
}
if (result.name.length == 0) {
ffStrbufAppendS(&result.name, "Cascadia Mono");
}
if (result.size < 0) {
result.size = 12;
}
char sizeStr[16];
snprintf(sizeStr, ARRAY_SIZE(sizeStr), "%g", result.size);
ffFontInitValues(&terminalFont->font, result.name.chars, sizeStr);
}
static void detectMintty(FFTerminalFontResult* terminalFont) {
+29 -42
View File
@@ -6,59 +6,46 @@
#include <libproc.h>
const char* ffTopGetProcessSnapshot(FFlist* snapshots, FFTopTypes showTypes) {
int request[] = { CTL_KERN, KERN_PROC, KERN_PROC_ALL };
size_t length;
if (sysctl(request, ARRAY_SIZE(request), nullptr, &length, nullptr, 0) != 0) {
return "sysctl({CTL_KERN, KERN_PROC, KERN_PROC_ALL, nullptr}) failed";
int npids = proc_listallpids(nullptr, 0);
if (npids <= 0) {
return "proc_listallpids(nullptr, 0) failed";
}
FF_AUTO_FREE pid_t* pids = malloc((uint32_t) (npids + npids / 8 + 1) * sizeof(pid_t));
npids = proc_listallpids(pids, npids);
if (npids <= 0) {
return "proc_listallpids(pids, bufferSize) failed";
}
// The process table may change between the two sysctl calls; retry with a larger buffer.
length += length / 8 + sizeof(struct kinfo_proc);
FF_AUTO_FREE struct kinfo_proc* processes = malloc(length);
if (sysctl(request, ARRAY_SIZE(request), processes, &length, nullptr, 0) != 0) {
return "sysctl({CTL_KERN, KERN_PROC, KERN_PROC_ALL, processes}) failed";
}
uint32_t count = (uint32_t) (length / sizeof(struct kinfo_proc));
uint32_t count = (uint32_t) npids;
for (uint32_t i = 0; i < count; ++i) {
const struct kinfo_proc* proc = &processes[i];
if (proc->kp_proc.p_flag & P_SYSTEM) {
pid_t pid = pids[i];
struct proc_taskallinfo proc;
if (proc_pidinfo(pid, PROC_PIDTASKALLINFO, 0, &proc, sizeof(proc)) != sizeof(proc)) {
continue;
}
pid_t pid = proc->kp_proc.p_pid;
struct rusage_info_v2 rusage;
if (proc_pid_rusage(pid, RUSAGE_INFO_V2, (rusage_info_t*) &rusage) != 0) {
continue; // The process may have exited
if (proc.pbsd.pbi_flags & PROC_FLAG_SYSTEM) {
continue;
}
FFTopProcessSnapshot* item = FF_LIST_ADD(FFTopProcessSnapshot, *snapshots);
ffStrbufInitS(&item->name, proc->kp_proc.p_comm);
ffStrbufInitS(&item->name, proc.pbsd.pbi_name);
if (item->name.length == 0) {
ffStrbufInitS(&item->name, proc.pbsd.pbi_comm);
}
item->pid = (uint32_t) pid;
// Note: Do NOT use proc->kp_proc.p_pctcpu for CPU usage. p_pctcpu is a
// decaying average (fixpt_t with FSCALE=2048) updated roughly once per
// second by the kernel. It is heavily smoothed, lags short bursts, has
// low resolution, and its multicore scaling (>100%) is inconsistent
// across XNU versions. It also breaks the unified model where
// FFTopProcessSnapshot.cpuTime is cumulative time and top.c computes
// (new - old) / elapsed * 100 for all platforms. proc_pid_rusage with
// RUSAGE_INFO_V2 provides precise cumulative ri_user_time +
// ri_system_time in nanoseconds and is the modern recommended API on
// Darwin, consistent with Linux/BSD differential sampling and accurate
// for the short waitTime interval (e.g. 100ms).
item->cpuTime = (rusage.ri_user_time + rusage.ri_system_time) / 1000000u; // ns -> ms
item->memBytes = rusage.ri_resident_size;
item->bytesRead = rusage.ri_diskio_bytesread;
item->bytesWritten = rusage.ri_diskio_byteswritten;
item->startTime = rusage.ri_proc_start_abstime;
item->threads = 0;
if (showTypes & FF_TOP_TYPE_THREADS) {
struct proc_taskinfo taskInfo;
if (proc_pidinfo(pid, PROC_PIDTASKINFO, 0, &taskInfo, sizeof(taskInfo)) == sizeof(taskInfo)) {
item->threads = (uint32_t) taskInfo.pti_threadnum;
item->cpuTime = (proc.ptinfo.pti_total_user + proc.ptinfo.pti_total_system) / 1000000u; // ns -> ms
item->memBytes = proc.ptinfo.pti_resident_size;
item->startTime = proc.pbsd.pbi_start_tvsec * 1000u + proc.pbsd.pbi_start_tvusec / 1000u; // convert to ms
item->threads = (uint32_t) proc.ptinfo.pti_threadnum;
if (showTypes & FF_TOP_TYPE_DISK) {
struct rusage_info_v2 rusage;
if (proc_pid_rusage(pid, RUSAGE_INFO_V2, (rusage_info_t*) &rusage) == 0) {
item->bytesRead = rusage.ri_diskio_bytesread;
item->bytesWritten = rusage.ri_diskio_byteswritten;
}
}
}
+25 -20
View File
@@ -1,24 +1,26 @@
#include "top.h"
#include "common/mallocHelper.h"
#include <sys/types.h>
#include <sys/param.h> // DEV_BSIZE
#include <sys/sysctl.h>
#include <kvm.h>
const char* ffTopGetProcessSnapshot(FFlist* snapshots, FFTopTypes showTypes) {
kvm_t* kd = kvm_open(nullptr, nullptr, nullptr, KVM_NO_FILES, nullptr);
if (!kd) {
return "kvm_open() failed";
int request[] = { CTL_KERN, KERN_PROC, KERN_PROC_ALL, 0, (int) sizeof(struct kinfo_proc), 0 };
size_t length = 0;
if (sysctl(request, ARRAY_SIZE(request), nullptr, &length, nullptr, 0) != 0) {
return "sysctl({CTL_KERN, KERN_PROC, KERN_PROC_ALL}, nullptr) failed";
}
int count = 0;
// KERN_PROC_ALL returns all user-level processes, excluding kernel processes and threads
const struct kinfo_proc* processes = kvm_getprocs(kd, KERN_PROC_ALL, 0, sizeof(struct kinfo_proc), &count);
if (!processes) {
kvm_close(kd);
return "kvm_getprocs() failed";
FF_AUTO_FREE struct kinfo_proc* processes = (struct kinfo_proc*) malloc(length);
request[5] = (int) (length / sizeof(struct kinfo_proc)); // count must be non-zero for data fetch
if (sysctl(request, ARRAY_SIZE(request), processes, &length, nullptr, 0) != 0) {
return "sysctl({CTL_KERN, KERN_PROC, KERN_PROC_ALL}, processes) failed";
}
int count = (int) (length / sizeof(struct kinfo_proc));
const uint32_t pageSize = instance.state.platform.sysinfo.pageSize;
for (int i = 0; i < count; ++i) {
@@ -41,21 +43,24 @@ const char* ffTopGetProcessSnapshot(FFlist* snapshots, FFTopTypes showTypes) {
}
if (showTypes & FF_TOP_TYPE_THREADS) {
int threadCount = 0;
const struct kinfo_proc* threads = kvm_getprocs(kd,
KERN_PROC_ALL | KERN_PROC_SHOW_THREADS, 0, sizeof(struct kinfo_proc), &threadCount);
if (threads) {
for (uint32_t i = 0; i < snapshots->length; ++i) {
FFTopProcessSnapshot* item = FF_LIST_GET(FFTopProcessSnapshot, *snapshots, i);
for (int j = 0; j < threadCount; ++j) {
if (threads[j].p_pid == (pid_t) item->pid && threads[j].p_tid != -1) {
++item->threads;
int threadRequest[] = { CTL_KERN, KERN_PROC, KERN_PROC_ALL | KERN_PROC_SHOW_THREADS, 0, (int) sizeof(struct kinfo_proc), 0 };
size_t threadLength = 0;
if (sysctl(threadRequest, ARRAY_SIZE(threadRequest), nullptr, &threadLength, nullptr, 0) == 0 && threadLength > 0) {
FF_AUTO_FREE struct kinfo_proc* threads = (struct kinfo_proc*) malloc(threadLength);
threadRequest[5] = (int) (threadLength / sizeof(struct kinfo_proc)); // count must be non-zero for data fetch
if (sysctl(threadRequest, ARRAY_SIZE(threadRequest), threads, &threadLength, nullptr, 0) == 0) {
int threadCount = (int) (threadLength / sizeof(struct kinfo_proc));
for (uint32_t i = 0; i < snapshots->length; ++i) {
FFTopProcessSnapshot* item = FF_LIST_GET(FFTopProcessSnapshot, *snapshots, i);
for (int j = 0; j < threadCount; ++j) {
if (threads[j].p_pid == (pid_t) item->pid && threads[j].p_tid != -1) {
++item->threads;
}
}
}
}
}
}
kvm_close(kd);
return nullptr;
}
-12
View File
@@ -3,18 +3,6 @@
#include "common/color.h"
static const FFlogo Z[] = {
#ifdef FASTFETCH_DATATEXT_LOGO_ZERENE
// Zerene
{
.names = { "Zerene" },
.lines = FASTFETCH_DATATEXT_LOGO_ZERENE,
.colors = {
FF_COLOR_FG_BLUE,
},
.colorKeys = FF_COLOR_FG_BLUE,
.colorTitle = FF_COLOR_FG_BLUE,
},
#endif
#ifdef FASTFETCH_DATATEXT_LOGO_ZORIN
// Zorin
{
-18
View File
@@ -1,18 +0,0 @@
MMM
MMMM
MMMMM
MMMMMM
MMMMMMa
MMMMMMMa
MMMMMMM
MMMMMM
MMMM
M
oxdo aMa
aMd aMMMMMMMMM
aMMMMM MMMMMMMMMMMMMMa
MMMMMMMa MMMMMMMMMMMMMMMa
MMMMMMMMa
MMMMMMMMa
MMMMMMM
MMMM
+3 -3
View File
@@ -287,11 +287,11 @@ void ffLogoPrintChars(const char* data, bool doColorReplacement) {
static void logoApplyColors(const FFlogo* logo, bool replacement) {
if (instance.config.display.colorTitle.length == 0) {
ffStrbufAppendS(&instance.config.display.colorTitle, logo->colorTitle ?: logo->colors[0]);
ffStrbufSetStatic(&instance.config.display.colorTitle, logo->colorTitle ?: logo->colors[0]);
}
if (instance.config.display.colorKeys.length == 0) {
ffStrbufAppendS(&instance.config.display.colorKeys, logo->colorKeys ?: logo->colors[1]);
ffStrbufSetStatic(&instance.config.display.colorKeys, logo->colorKeys ?: logo->colors[1]);
}
if (replacement) {
@@ -300,7 +300,7 @@ static void logoApplyColors(const FFlogo* logo, bool replacement) {
const char* const* colors = logo->colors;
for (int i = 0; *colors != nullptr && i < FASTFETCH_LOGO_MAX_COLORS; i++, colors++) {
if (options->colors[i].length == 0) {
ffStrbufAppendS(&options->colors[i], *colors);
ffStrbufSetStatic(&options->colors[i], *colors);
}
}
}