mirror of
https://github.com/fastfetch-cli/fastfetch.git
synced 2026-10-28 07:44:06 +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_config.h.in fastfetch_config.h @ONLY)
|
||||||
configure_file(src/fastfetch_datatext.h.in fastfetch_datatext.h @ONLY)
|
configure_file(src/fastfetch_datatext.h.in fastfetch_datatext.h @ONLY)
|
||||||
if(APPLE)
|
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)
|
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()
|
endif()
|
||||||
|
|
||||||
####################
|
####################
|
||||||
@@ -2244,6 +2266,15 @@ set(CMAKE_CXX_IMPLICIT_LINK_DIRECTORIES "")
|
|||||||
set_target_properties(fastfetch PROPERTIES LINKER_LANGUAGE C)
|
set_target_properties(fastfetch PROPERTIES LINKER_LANGUAGE C)
|
||||||
|
|
||||||
if(WIN32)
|
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
|
target_sources(fastfetch
|
||||||
PRIVATE src/common/windows/version.rc
|
PRIVATE src/common/windows/version.rc
|
||||||
)
|
)
|
||||||
@@ -2251,6 +2282,15 @@ elseif(APPLE)
|
|||||||
target_link_options(fastfetch
|
target_link_options(fastfetch
|
||||||
PRIVATE LINKER:-sectcreate,__TEXT,__info_plist,Info.plist
|
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()
|
endif()
|
||||||
|
|
||||||
if(BINARY_LINK_TYPE STREQUAL "static")
|
if(BINARY_LINK_TYPE STREQUAL "static")
|
||||||
@@ -2276,8 +2316,15 @@ if (BUILD_FLASHFETCH)
|
|||||||
PRIVATE src/common/windows/version.rc
|
PRIVATE src/common/windows/version.rc
|
||||||
)
|
)
|
||||||
elseif(APPLE)
|
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
|
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()
|
endif()
|
||||||
|
|
||||||
|
|||||||
@@ -3,7 +3,7 @@
|
|||||||
<plist version="1.0">
|
<plist version="1.0">
|
||||||
<dict>
|
<dict>
|
||||||
<key>CFBundleIdentifier</key>
|
<key>CFBundleIdentifier</key>
|
||||||
<string>fastfetch</string>
|
<string>@BUNDLE_ID@</string>
|
||||||
<key>CFBundleName</key>
|
<key>CFBundleName</key>
|
||||||
<string>@PROJECT_NAME@</string>
|
<string>@PROJECT_NAME@</string>
|
||||||
<key>CFBundleShortVersionString</key>
|
<key>CFBundleShortVersionString</key>
|
||||||
|
|||||||
@@ -110,106 +110,9 @@ static const char* getMediaByMediaRemote(FFMediaResult* result, bool saveCover)
|
|||||||
return error;
|
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) {
|
void ffDetectMediaImpl(FFMediaResult* media, bool saveCover) {
|
||||||
const char* error;
|
const char* error = getMediaByMediaRemote(media, saveCover);
|
||||||
if (@available(macOS 15.4, *)) {
|
|
||||||
error = getMediaByAuthorizedProcess(media, saveCover);
|
|
||||||
} else {
|
|
||||||
error = getMediaByMediaRemote(media, saveCover);
|
|
||||||
}
|
|
||||||
if (error) {
|
if (error) {
|
||||||
ffStrbufAppendS(&media->error, error);
|
ffStrbufAppendS(&media->error, error);
|
||||||
} else {
|
} else {
|
||||||
|
|||||||
Reference in New Issue
Block a user