mirror of
https://github.com/fastfetch-cli/fastfetch.git
synced 2026-11-10 13:37:25 +01:00
Compare commits
16
Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
e3137cb8d3 | ||
|
|
cd5a2def9a | ||
|
|
8334035927 | ||
|
|
1ee8528f10 | ||
|
|
ced49ab2a4 | ||
|
|
ec8b9be354 | ||
|
|
19725cd2f7 | ||
|
|
20c155e5b4 | ||
|
|
61e3f6108c | ||
|
|
eff8514536 | ||
|
|
f37a5a2543 | ||
|
|
9a9f82b59e | ||
|
|
29818d2dca | ||
|
|
222fe7e3f9 | ||
|
|
7d0256fb85 | ||
|
|
a70c37fdaf |
No files matched your search
@@ -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
|
||||
|
||||
@@ -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: |
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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'
|
||||
|
||||
@@ -1,3 +1,8 @@
|
||||
# Unreleased
|
||||
|
||||
Features:
|
||||
* Added CPU name and frequency detection support on SPARC. (CPU, Linux)
|
||||
|
||||
# 2.68.1
|
||||
|
||||
Changes:
|
||||
|
||||
+4
-4
@@ -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"
|
||||
)
|
||||
|
||||
Vendored
+1
-1
@@ -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"
|
||||
}
|
||||
Vendored
+700
-285
File diff suppressed because it is too large.
Load diff
Vendored
+911
-674
File diff suppressed because it is too large.
Load diff
@@ -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);
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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
@@ -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;
|
||||
}
|
||||
|
||||
@@ -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
|
||||
}
|
||||
|
||||
|
||||
@@ -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"
|
||||
|
||||
@@ -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
@@ -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) {
|
||||
|
||||
@@ -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,
|
||||
})) {
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
@@ -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) {
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
@@ -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
|
||||
{
|
||||
|
||||
@@ -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
@@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Reference in new issue
Block a user