Common (macOS): unifies AppBundle version detection code

This commit is contained in:
Carter Li
2026-05-22 13:37:48 +08:00
parent 2751737ebe
commit 9856d97bd1
5 changed files with 95 additions and 79 deletions
+1
View File
@@ -954,6 +954,7 @@ elseif(APPLE)
src/common/apple/cf_helpers.c
src/common/apple/osascript.m
src/common/apple/smc_temps.c
src/common/apple/version.m
src/detection/battery/battery_apple.c
src/detection/bluetooth/bluetooth_apple.m
src/detection/bluetoothradio/bluetoothradio_apple.m
+5
View File
@@ -0,0 +1,5 @@
#pragma once
#include "fastfetch.h"
bool ffGetAppNameAndVersion(const char* exePath, FFstrbuf* retName, FFstrbuf* retVersion);
+43
View File
@@ -0,0 +1,43 @@
#include "common/apple/version.h"
#include "common/stringUtils.h"
#import <Foundation/Foundation.h>
#define APP_CONTENTS_MACOS ".app/Contents/MacOS"
bool ffGetAppNameAndVersion(const char* exePath, FFstrbuf* retName, FFstrbuf* retVersion) {
char* lastSlash = strrchr(exePath, '/');
if (!lastSlash) {
return false;
}
if ((size_t) (lastSlash - exePath) > strlen("X" APP_CONTENTS_MACOS) && memcmp(lastSlash - strlen(APP_CONTENTS_MACOS), APP_CONTENTS_MACOS, strlen(APP_CONTENTS_MACOS)) != 0) {
return false;
}
lastSlash -= strlen("MacOS");
char infoPlistPath[PATH_MAX];
memcpy(infoPlistPath, exePath, lastSlash - exePath);
memcpy(infoPlistPath + (lastSlash - exePath), "Info.plist", sizeof("Info.plist")); // X.app/Contents/Info.plist
NSError* error;
NSDictionary* dict = [NSDictionary dictionaryWithContentsOfURL:[NSURL fileURLWithPath:@(infoPlistPath)]
error:&error];
if (!dict) {
return false;
}
if (retName) {
NSString* bundleName = dict[@"CFBundleDisplayName"] ?: dict[@"CFBundleName"];
if (bundleName) {
ffStrbufSetS(retName, bundleName.UTF8String);
}
}
if (retVersion) {
NSString* bundleVersion = dict[@"CFBundleShortVersionString"];
if (bundleVersion) {
ffStrbufSetS(retVersion, bundleVersion.UTF8String);
}
}
return true;
}
+8 -25
View File
@@ -32,6 +32,8 @@ static bool getFileVersion(const FFstrbuf* exePath, const wchar_t* stringName, F
#elif __HAIKU__
#include "common/haiku/version.h"
#elif __APPLE__
#include "common/apple/version.h"
#endif
static bool getExeVersionRaw(FFstrbuf* exe, FFstrbuf* version) {
@@ -616,31 +618,8 @@ static bool getTerminalVersionKitty(FFstrbuf* exe, FFstrbuf* version) {
}
}
#elif __APPLE__
if (ffStrbufEndsWithS(exe, "/kitty.app/Contents/MacOS/kitty")) {
ffStrbufSet(version, exe);
ffStrbufSubstrBeforeLastC(version, '/');
ffStrbufSubstrBeforeLastC(version, '/');
ffStrbufAppendS(version, "/Info.plist");
char buf[4096];
ssize_t size = ffReadFileData(version->chars, ARRAY_SIZE(buf) - 1, buf);
if (size > 0) {
buf[size] = '\0';
const char* p = strstr(buf, "<key>CFBundleShortVersionString</key>");
if (p) {
p += strlen("<key>CFBundleShortVersionString</key>");
p = strchr(p, '>');
if (p) {
p++;
const char* end = strchr(p, '<');
if (end) {
ffStrbufSetNS(version, (uint32_t) (end - p), p);
return true;
}
}
}
}
ffStrbufClear(version);
if (ffGetAppNameAndVersion(exe->chars, NULL, version)) {
return true;
}
#endif
@@ -948,6 +927,10 @@ bool fftsGetTerminalVersion(FFstrbuf* processName, FF_A_UNUSED FFstrbuf* exe, FF
return getFileVersion(exe, NULL, version);
#elif __APPLE__
return ffGetAppNameAndVersion(exe->chars, NULL, version);
#else
return false;
+38 -54
View File
@@ -3,77 +3,62 @@
#include "common/sysctl.h"
#include "common/mallocHelper.h"
#include "common/stringUtils.h"
#include "common/apple/version.h"
#include <ctype.h>
#include <libproc.h>
#import <Foundation/Foundation.h>
const char* ffDetectWMPlugin(FFstrbuf* pluginName)
{
int request[] = {CTL_KERN, KERN_PROC, KERN_PROC_ALL};
const char* ffDetectWMPlugin(FFstrbuf* pluginName) {
int request[] = { CTL_KERN, KERN_PROC, KERN_PROC_ALL };
u_int requestLength = ARRAY_SIZE(request);
size_t length = 0;
FF_AUTO_FREE struct kinfo_proc* processes = ffSysctlGetData(request, requestLength, &length);
if(processes == NULL)
if (processes == NULL) {
return "sysctl(CTL_KERN, KERN_PROC, KERN_PROC_ALL) failed";
}
assert(length % sizeof(struct kinfo_proc) == 0);
for(size_t i = 0; i < length / sizeof(struct kinfo_proc); i++)
{
for (size_t i = 0; i < length / sizeof(struct kinfo_proc); i++) {
const struct kinfo_proc* proc = &processes[i];
if (proc->kp_eproc.e_ppid != 1) continue;
if (proc->kp_eproc.e_ppid != 1) {
continue;
}
const char* comm = proc->kp_proc.p_comm;
if(
if (
!ffStrEqualsIgnCase(comm, "rectangle") && // 28.6k
!ffStrEqualsIgnCase(comm, "yabai") && // 28.4k
!ffStrEqualsIgnCase(comm, "yabai") && // 28.4k
!ffStrEqualsIgnCase(comm, "aerospace") && // 19.6k
!ffStrEqualsIgnCase(comm, "amethyst") && // 16k
!ffStrEqualsIgnCase(comm, "glazewm") && // 11.6k
!ffStrEqualsIgnCase(comm, "amethyst") && // 16k
!ffStrEqualsIgnCase(comm, "glazewm") && // 11.6k
#if 0
#if 0
// Unmaintained
!ffStrEqualsIgnCase(comm, "spectacle") && // 13.6k
!ffStrEqualsIgnCase(comm, "chunkwm") && // repo deleted; was https://github.com/koekeishiya/chunkwm
!ffStrEqualsIgnCase(comm, "kwm") && // repo deleted; was https://github.com/koekeishiya/kwm
#endif
true
) continue;
#endif
true)
continue;
if (instance.config.general.detectVersion)
{
if (instance.config.general.detectVersion) {
char buf[PROC_PIDPATHINFO_MAXSIZE];
int length = proc_pidpath(proc->kp_proc.p_pid, buf, ARRAY_SIZE(buf) - strlen("Info.plist"));
if (length > 0)
{
char* lastSlash = strrchr(buf, '/');
if (lastSlash)
{
*lastSlash = '\0';
if (ffStrEndsWith(buf, ".app/Contents/MacOS"))
{
lastSlash -= strlen("MacOS");
strcpy(lastSlash, "Info.plist"); // X.app/Contents/Info.plist
NSError* error;
NSDictionary* dict = [NSDictionary dictionaryWithContentsOfURL:[NSURL fileURLWithPath:@(buf)]
error:&error];
if (dict)
{
NSString* name = dict[@"CFBundleDisplayName"] ?: dict[@"CFBundleName"];
ffStrbufSetS(pluginName, name.UTF8String ?: comm);
NSString* version = dict[@"CFBundleShortVersionString"];
if (version)
{
ffStrbufAppendC(pluginName, ' ');
ffStrbufAppendS(pluginName, version.UTF8String);
}
break;
}
if (length > 0) {
buf[length] = '\0';
FF_STRBUF_AUTO_DESTROY pluginVersion = ffStrbufCreate();
if (ffGetAppNameAndVersion(buf, pluginName, &pluginVersion)) {
if (pluginName->length == 0) {
ffStrbufSetS(pluginName, comm);
}
if (pluginVersion.length > 0) {
ffStrbufAppendC(pluginName, ' ');
ffStrbufAppend(pluginName, &pluginVersion);
}
break;
}
}
}
@@ -86,24 +71,23 @@ const char* ffDetectWMPlugin(FFstrbuf* pluginName)
return NULL;
}
const char* ffDetectWMVersion(const FFstrbuf* wmName, FFstrbuf* result, FF_A_UNUSED FFWMOptions* options)
{
if (!wmName)
const char* ffDetectWMVersion(const FFstrbuf* wmName, FFstrbuf* result, FF_A_UNUSED FFWMOptions* options) {
if (!wmName) {
return "No WM detected";
}
if (ffStrbufEqualS(wmName, "WindowServer"))
{
if (ffStrbufEqualS(wmName, "WindowServer")) {
NSError* error;
NSDictionary* dict = [NSDictionary dictionaryWithContentsOfURL:[NSURL fileURLWithPath:@"/System/Library/PrivateFrameworks/SkyLight.framework/Resources/version.plist" isDirectory:NO]
error:&error];
if (!dict)
{
error:&error];
if (!dict) {
dict = [NSDictionary dictionaryWithContentsOfURL:[NSURL fileURLWithPath:@"/System/Library/Frameworks/ApplicationServices.framework/Frameworks/CoreGraphics.framework/Resources/version.plist" isDirectory:NO]
error:&error];
error:&error];
}
if (dict)
if (dict) {
ffStrbufSetS(result, ((NSString*) dict[@"CFBundleShortVersionString"]).UTF8String);
}
}
return NULL;