mirror of
https://github.com/fastfetch-cli/fastfetch.git
synced 2026-10-28 02:31:25 +01:00
Media (macOS): don't spawn child process
This commit is contained in:
+48
-1
@@ -454,7 +454,29 @@ fastfetch_encode_c_string("${DATATEXT_JSON_HELP}" DATATEXT_JSON_HELP)
|
||||
configure_file(src/fastfetch_config.h.in fastfetch_config.h @ONLY)
|
||||
configure_file(src/fastfetch_datatext.h.in fastfetch_datatext.h @ONLY)
|
||||
if(APPLE)
|
||||
# `mediaremoted` serves now-playing data only to clients whose bundle id starts
|
||||
# with "com.apple.": -[MRDMediaRemoteClient isAllowedAccessToDataFromPlayerPath:]
|
||||
# accepts the 0x200 entitlement bit either for the private
|
||||
# com.apple.mediaremote.now-playing-read-access entitlement or for that prefix,
|
||||
# and it derives the client's identity from the code signature / XPC audit token
|
||||
# rather than from the process's own NSBundle. Both the embedded
|
||||
# CFBundleIdentifier and the ad-hoc signing identifier below therefore have to
|
||||
# carry the prefix; keep them in sync through these variables.
|
||||
set(FASTFETCH_BUNDLE_ID "com.apple.fastfetch")
|
||||
set(FLASHFETCH_BUNDLE_ID "com.apple.flashfetch")
|
||||
# configure_file() inputs are not configure dependencies on their own, so
|
||||
# without this an edit to Info.plist.in never even regenerates Info.plist.
|
||||
set_property(DIRECTORY APPEND PROPERTY CMAKE_CONFIGURE_DEPENDS "${CMAKE_CURRENT_SOURCE_DIR}/src/common/apple/Info.plist.in")
|
||||
# Each executable gets its own plist: the file is embedded with -sectcreate and read
|
||||
# back by the system as the bundle id, so two targets sharing one would leave one of
|
||||
# them advertising an identifier that its code signature does not match.
|
||||
set(BUNDLE_ID "${FASTFETCH_BUNDLE_ID}")
|
||||
configure_file(src/common/apple/Info.plist.in Info.plist @ONLY)
|
||||
if(BUILD_FLASHFETCH)
|
||||
set(BUNDLE_ID "${FLASHFETCH_BUNDLE_ID}")
|
||||
configure_file(src/common/apple/Info.plist.in Info.plist.flashfetch @ONLY)
|
||||
endif()
|
||||
unset(BUNDLE_ID)
|
||||
endif()
|
||||
|
||||
####################
|
||||
@@ -2244,6 +2266,15 @@ set(CMAKE_CXX_IMPLICIT_LINK_DIRECTORIES "")
|
||||
set_target_properties(fastfetch PROPERTIES LINKER_LANGUAGE C)
|
||||
|
||||
if(WIN32)
|
||||
# version.rc embeds manifest.xml (RT_MANIFEST) and logo.ico (ICON) as *resource
|
||||
# references*, not as #includes, so the resource compiler never reports them and
|
||||
# CMake would keep a stale .res when only the manifest or the icon changes. Both
|
||||
# are plain files rather than generated ones, so declare them by hand; the source
|
||||
# file property is per-path, which is why flashfetch below needs no repeat.
|
||||
set_property(SOURCE src/common/windows/version.rc APPEND PROPERTY OBJECT_DEPENDS
|
||||
"${CMAKE_CURRENT_SOURCE_DIR}/src/common/windows/manifest.xml"
|
||||
"${CMAKE_CURRENT_SOURCE_DIR}/src/common/windows/logo.ico"
|
||||
)
|
||||
target_sources(fastfetch
|
||||
PRIVATE src/common/windows/version.rc
|
||||
)
|
||||
@@ -2251,6 +2282,15 @@ elseif(APPLE)
|
||||
target_link_options(fastfetch
|
||||
PRIVATE LINKER:-sectcreate,__TEXT,__info_plist,Info.plist
|
||||
)
|
||||
# `LINKER:` options are handed to ld verbatim and are not registered as build
|
||||
# dependencies, so without this ninja never relinks when Info.plist changes.
|
||||
set_property(TARGET fastfetch APPEND PROPERTY LINK_DEPENDS "${CMAKE_CURRENT_BINARY_DIR}/Info.plist")
|
||||
# ld derives the ad-hoc code-signing identifier from the output file name and
|
||||
# ignores the embedded CFBundleIdentifier, so set it explicitly.
|
||||
add_custom_command(TARGET fastfetch POST_BUILD
|
||||
COMMAND codesign --force --sign - --identifier "${FASTFETCH_BUNDLE_ID}" "$<TARGET_FILE:fastfetch>"
|
||||
VERBATIM
|
||||
)
|
||||
endif()
|
||||
|
||||
if(BINARY_LINK_TYPE STREQUAL "static")
|
||||
@@ -2276,8 +2316,15 @@ if (BUILD_FLASHFETCH)
|
||||
PRIVATE src/common/windows/version.rc
|
||||
)
|
||||
elseif(APPLE)
|
||||
# flashfetch embeds its own plist so that its CFBundleIdentifier matches the
|
||||
# identifier it is signed with below (see the APPLE block near the top).
|
||||
target_link_options(flashfetch
|
||||
PRIVATE LINKER:-sectcreate,__TEXT,__info_plist,Info.plist
|
||||
PRIVATE LINKER:-sectcreate,__TEXT,__info_plist,Info.plist.flashfetch
|
||||
)
|
||||
set_property(TARGET flashfetch APPEND PROPERTY LINK_DEPENDS "${CMAKE_CURRENT_BINARY_DIR}/Info.plist.flashfetch")
|
||||
add_custom_command(TARGET flashfetch POST_BUILD
|
||||
COMMAND codesign --force --sign - --identifier "${FLASHFETCH_BUNDLE_ID}" "$<TARGET_FILE:flashfetch>"
|
||||
VERBATIM
|
||||
)
|
||||
endif()
|
||||
|
||||
|
||||
@@ -3,7 +3,7 @@
|
||||
<plist version="1.0">
|
||||
<dict>
|
||||
<key>CFBundleIdentifier</key>
|
||||
<string>fastfetch</string>
|
||||
<string>@BUNDLE_ID@</string>
|
||||
<key>CFBundleName</key>
|
||||
<string>@PROJECT_NAME@</string>
|
||||
<key>CFBundleShortVersionString</key>
|
||||
|
||||
@@ -110,106 +110,9 @@ static const char* getMediaByMediaRemote(FFMediaResult* result, bool saveCover)
|
||||
return error;
|
||||
}
|
||||
|
||||
#if !FF_MODULE_DISABLE_MEDIA
|
||||
// Called from the Perl helper process through `DynaLoader::dl_install_xsub`. That mechanism installs a
|
||||
// plain C function as an XSUB, so Perl calls it with the `CV*` in the first parameter slot and no Perl
|
||||
// argument ever reaches a parameter — a `bool saveCover` parameter read as a pointer and was always
|
||||
// truthy. The flag has to travel in the environment instead; see `getMediaByAuthorizedProcess`.
|
||||
[[gnu::visibility("default"), gnu::used]] int ffPrintMediaByMediaRemote(void) {
|
||||
const char* saveCoverEnv = getenv("FF_MEDIA_REMOTE_SAVE_COVER");
|
||||
bool saveCover = saveCoverEnv && saveCoverEnv[0] == '1';
|
||||
|
||||
FFMediaResult media = {
|
||||
.status = ffStrbufCreate(),
|
||||
.song = ffStrbufCreate(),
|
||||
.artist = ffStrbufCreate(),
|
||||
.album = ffStrbufCreate(),
|
||||
.playerId = ffStrbufCreate(),
|
||||
.player = ffStrbufCreate(),
|
||||
.cover = ffStrbufCreate(),
|
||||
};
|
||||
if (getMediaByMediaRemote(&media, saveCover) != nullptr) {
|
||||
return 1;
|
||||
}
|
||||
ffStrbufAppendC(&media.status, '\n');
|
||||
ffStrbufAppend(&media.status, &media.song);
|
||||
ffStrbufAppendC(&media.status, '\n');
|
||||
ffStrbufAppend(&media.status, &media.artist);
|
||||
ffStrbufAppendC(&media.status, '\n');
|
||||
ffStrbufAppend(&media.status, &media.album);
|
||||
ffStrbufAppendC(&media.status, '\n');
|
||||
ffStrbufAppend(&media.status, &media.playerId);
|
||||
ffStrbufAppendC(&media.status, '\n');
|
||||
ffStrbufAppend(&media.status, &media.player);
|
||||
ffStrbufAppendC(&media.status, '\n');
|
||||
if (saveCover) {
|
||||
ffStrbufAppend(&media.status, &media.cover);
|
||||
}
|
||||
ffStrbufAppendC(&media.status, '\n');
|
||||
ffStrbufAppendUInt(&media.status, media.position);
|
||||
ffStrbufAppendC(&media.status, '\n');
|
||||
ffStrbufAppendUInt(&media.status, media.length);
|
||||
write(STDOUT_FILENO, media.status.chars, media.status.length);
|
||||
ffStrbufDestroy(&media.status);
|
||||
ffStrbufDestroy(&media.song);
|
||||
ffStrbufDestroy(&media.artist);
|
||||
ffStrbufDestroy(&media.album);
|
||||
ffStrbufDestroy(&media.playerId);
|
||||
ffStrbufDestroy(&media.player);
|
||||
ffStrbufDestroy(&media.cover);
|
||||
return 0;
|
||||
}
|
||||
#endif
|
||||
|
||||
static const char* getMediaByAuthorizedProcess(FFMediaResult* result, bool saveCover) {
|
||||
// #1737
|
||||
// `DynaLoader::dl_install_xsub` hands the C function the `CV*` as its first parameter, so the flag
|
||||
// cannot be passed as a Perl argument — `ffPrintMediaByMediaRemote` reads it from the environment,
|
||||
// which the child process inherits.
|
||||
FF_STRBUF_AUTO_DESTROY script = ffStrbufCreateF("$ENV{'FF_MEDIA_REMOTE_SAVE_COVER'}='%c';\
|
||||
require DynaLoader;\
|
||||
my $so = DynaLoader::dl_load_file('%s', 0);\
|
||||
my $sym = DynaLoader::dl_find_symbol($so, 'ffPrintMediaByMediaRemote');\
|
||||
DynaLoader::dl_install_xsub('fn', $sym, __FILE__);\
|
||||
exit(fn())",
|
||||
saveCover ? '1' : '0',
|
||||
instance.state.platform.exePath.chars);
|
||||
FF_STRBUF_AUTO_DESTROY buffer = ffStrbufCreate();
|
||||
|
||||
const char* error = ffProcessAppendStdOut(
|
||||
&buffer,
|
||||
(char* const[]) { "/usr/bin/perl", // Must be signed by Apple. Homebrew version doesn't work
|
||||
"-e",
|
||||
script.chars,
|
||||
nil });
|
||||
if (error) {
|
||||
return error;
|
||||
}
|
||||
if (buffer.length == 0) {
|
||||
return "No media found";
|
||||
}
|
||||
|
||||
// status\ntitle\nartist\nalbum\nbundleName\nappName\ncoverPath\nelapsedTimeMS\ndurationMS
|
||||
FFstrbuf* const strList[] = { &result->status, &result->song, &result->artist, &result->album, &result->playerId, &result->player, &result->cover };
|
||||
char* line = nullptr;
|
||||
size_t len = 0;
|
||||
for (uint32_t i = 0; i < ARRAY_SIZE(strList) && ffStrbufGetline(&line, &len, &buffer); ++i) {
|
||||
ffStrbufSetS(strList[i], line);
|
||||
}
|
||||
uint32_t* const numList[] = { &result->position, &result->length };
|
||||
for (uint32_t i = 0; i < ARRAY_SIZE(numList) && ffStrbufGetline(&line, &len, &buffer); ++i) {
|
||||
*numList[i] = (uint32_t) strtoul(line, nullptr, 10);
|
||||
}
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
void ffDetectMediaImpl(FFMediaResult* media, bool saveCover) {
|
||||
const char* error;
|
||||
if (@available(macOS 15.4, *)) {
|
||||
error = getMediaByAuthorizedProcess(media, saveCover);
|
||||
} else {
|
||||
error = getMediaByMediaRemote(media, saveCover);
|
||||
}
|
||||
const char* error = getMediaByMediaRemote(media, saveCover);
|
||||
|
||||
if (error) {
|
||||
ffStrbufAppendS(&media->error, error);
|
||||
} else {
|
||||
|
||||
Reference in New Issue
Block a user