From 482e5a5eff361c05949d3cf5dd48f43e6c0757d4 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E6=9D=8E=E9=80=9A=E6=B4=B2?= Date: Tue, 20 Sep 2022 16:50:15 +0800 Subject: [PATCH 1/4] util/cf_helpers: rename; return error info if failed --- CMakeLists.txt | 2 +- src/detection/battery/battery_apple.c | 12 +++--- src/detection/gpu/gpu_apple.c | 6 +-- src/detection/media/media_apple.m | 2 +- src/util/apple/cf_helpers.c | 59 +++++++++++++++++++++++++++ src/util/apple/cf_helpers.h | 15 +++++++ src/util/apple/cfdict_helpers.c | 57 -------------------------- src/util/apple/cfdict_helpers.h | 13 ------ 8 files changed, 85 insertions(+), 81 deletions(-) create mode 100644 src/util/apple/cf_helpers.c create mode 100644 src/util/apple/cf_helpers.h delete mode 100644 src/util/apple/cfdict_helpers.c delete mode 100644 src/util/apple/cfdict_helpers.h diff --git a/CMakeLists.txt b/CMakeLists.txt index c934526b2..d0a3c9643 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -272,7 +272,7 @@ endif() if(APPLE) list(APPEND LIBFASTFETCH_SRC - src/util/apple/cfdict_helpers.c + src/util/apple/cf_helpers.c src/util/apple/osascript.m src/detection/host/host_apple.c src/detection/os/os_apple.c diff --git a/src/detection/battery/battery_apple.c b/src/detection/battery/battery_apple.c index 3af71bf56..979367662 100644 --- a/src/detection/battery/battery_apple.c +++ b/src/detection/battery/battery_apple.c @@ -1,6 +1,6 @@ #include "fastfetch.h" #include "battery.h" -#include "util/apple/cfdict_helpers.h" +#include "util/apple/cf_helpers.h" #include @@ -31,14 +31,14 @@ const char* ffDetectBatteryImpl(FFinstance* instance, FFlist* results) BatteryResult* battery = ffListAdd(results); ffStrbufInit(&battery->capacity); int currentCapacity, maxCapacity; - if(ffCfDictGetInt(properties, CFSTR("CurrentCapacity"), ¤tCapacity) && - ffCfDictGetInt(properties, CFSTR("MaxCapacity"), &maxCapacity)) + if(!ffCfDictGetInt(properties, CFSTR("CurrentCapacity"), ¤tCapacity) && + !ffCfDictGetInt(properties, CFSTR("MaxCapacity"), &maxCapacity)) ffStrbufAppendF(&battery->capacity, "%.0f", currentCapacity * 100.0 / maxCapacity); ffStrbufInit(&battery->manufacturer); ffStrbufInit(&battery->modelName); ffStrbufInit(&battery->technology); - if (ffCfDictGetBool(properties, CFSTR("built-in"), &boolValue) && boolValue) + if (!ffCfDictGetBool(properties, CFSTR("built-in"), &boolValue) && boolValue) { ffStrbufAppendS(&battery->manufacturer, "Apple Inc."); ffStrbufAppendS(&battery->modelName, "Builtin"); @@ -52,9 +52,9 @@ const char* ffDetectBatteryImpl(FFinstance* instance, FFlist* results) } ffStrbufInit(&battery->status); - if (ffCfDictGetBool(properties, CFSTR("FullyCharged"), &boolValue) && boolValue) + if (!ffCfDictGetBool(properties, CFSTR("FullyCharged"), &boolValue) && boolValue) ffStrbufAppendS(&battery->status, "Fully charged"); - else if (ffCfDictGetBool(properties, CFSTR("IsCharging"), &boolValue) && boolValue) + else if (!ffCfDictGetBool(properties, CFSTR("IsCharging"), &boolValue) && boolValue) ffStrbufAppendS(&battery->status, "Charging"); else ffStrbufAppendS(&battery->status, ""); diff --git a/src/detection/gpu/gpu_apple.c b/src/detection/gpu/gpu_apple.c index e756b9467..445df6831 100644 --- a/src/detection/gpu/gpu_apple.c +++ b/src/detection/gpu/gpu_apple.c @@ -1,7 +1,7 @@ #include "gpu.h" #include "common/library.h" #include "detection/cpu/cpu.h" -#include "util/apple/cfdict_helpers.h" +#include "util/apple/cf_helpers.h" #include @@ -34,7 +34,7 @@ const char* ffDetectGPUImpl(FFlist* gpus, const FFinstance* instance) ffStrbufInit(&gpu->name); //IOAccelerator returns model property for Apple Silicon, but not for Intel Iris GPUs. //Still needs testing for AMD's - if(!ffCfDictGetString(properties, CFSTR("model"), &gpu->name)) + if(ffCfDictGetString(properties, CFSTR("model"), &gpu->name)) { CFRelease(properties); @@ -49,7 +49,7 @@ const char* ffDetectGPUImpl(FFlist* gpus, const FFinstance* instance) ffCfDictGetString(properties, CFSTR("model"), &gpu->name); } - if(!ffCfDictGetInt(properties, CFSTR("gpu-core-count"), &gpu->coreCount)) + if(ffCfDictGetInt(properties, CFSTR("gpu-core-count"), &gpu->coreCount)) gpu->coreCount = FF_GPU_CORE_COUNT_UNSET; gpu->temperature = FF_GPU_TEMP_UNSET; diff --git a/src/detection/media/media_apple.m b/src/detection/media/media_apple.m index ab9b34580..99f43ca49 100644 --- a/src/detection/media/media_apple.m +++ b/src/detection/media/media_apple.m @@ -1,7 +1,7 @@ #include "fastfetch.h" #include "detection/media/media.h" #include "common/library.h" -#include "util/apple/cfdict_helpers.h" +#include "util/apple/cf_helpers.h" #import #import diff --git a/src/util/apple/cf_helpers.c b/src/util/apple/cf_helpers.c new file mode 100644 index 000000000..574ce92d3 --- /dev/null +++ b/src/util/apple/cf_helpers.c @@ -0,0 +1,59 @@ +#include "cf_helpers.h" + +const char* ffCfStrGetString(CFStringRef str, FFstrbuf* result) +{ + uint32_t length = (uint32_t)CFStringGetLength(str); + //CFString stores UTF16 characters, therefore may require larger buffer to convert to UTF8 string + ffStrbufEnsureFree(result, length * 2); + if(!CFStringGetCString(str, result->chars, result->allocated, kCFStringEncodingUTF8)) + return "CFStringGetCString() failed"; + // CFStringGetCString ensures the buffer is NUL terminated + // https://developer.apple.com/documentation/corefoundation/1542721-cfstringgetcstring + result->length = (uint32_t) strnlen(result->chars, (uint32_t)result->allocated); + return NULL; +} + +const char* ffCfDictGetString(CFDictionaryRef dict, CFStringRef key, FFstrbuf* result) +{ + CFTypeRef cf = (CFTypeRef)CFDictionaryGetValue(dict, key); + if(cf == NULL) + return "CFDictionaryGetValue() failed"; + + if(CFGetTypeID(cf) == CFStringGetTypeID()) + { + return ffCfStrGetString((CFStringRef)cf, result); + } + else if(CFGetTypeID(cf) == CFDataGetTypeID()) + { + CFDataRef cfData = (CFDataRef)cf; + uint32_t length = (uint32_t)CFDataGetLength(cfData); + ffStrbufEnsureFree(result, length + 1); + CFDataGetBytes(cfData, CFRangeMake(0, length), (uint8_t*)result->chars); + result->length = (uint32_t)strnlen(result->chars, length); + result->chars[result->length] = '\0'; + return NULL; + } + + return "TypeID is neither 'CFString' nor 'CFData'"; +} + +const char* ffCfDictGetBool(CFDictionaryRef dict, CFStringRef key, bool* result) +{ + CFBooleanRef cf = (CFBooleanRef)CFDictionaryGetValue(dict, key); + if(cf == NULL || CFGetTypeID(cf) != CFBooleanGetTypeID()) + return "TypeID is not 'CFBoolean'"; + + *result = CFBooleanGetValue(cf); + return NULL; +} + +const char* ffCfDictGetInt(CFDictionaryRef dict, CFStringRef key, int* result) +{ + CFNumberRef cf = (CFNumberRef)CFDictionaryGetValue(dict, key); + if (cf == NULL || CFGetTypeID(cf) != CFNumberGetTypeID()) + return "TypeID is not 'CFNumber'"; + + if(!CFNumberGetValue(cf, kCFNumberSInt32Type, result)) + return "Number type is not SInt32"; + return NULL; +} diff --git a/src/util/apple/cf_helpers.h b/src/util/apple/cf_helpers.h new file mode 100644 index 000000000..49aa405f9 --- /dev/null +++ b/src/util/apple/cf_helpers.h @@ -0,0 +1,15 @@ +#pragma once + +#ifndef FASTFETCH_INCLUDED_cf_helpers +#define FASTFETCH_INCLUDED_cf_helpers + +#include "fastfetch.h" +#include + +//Return error info if failed, NULL otherwise +const char* ffCfStrGetString(CFStringRef str, FFstrbuf* result); +const char* ffCfDictGetString(CFDictionaryRef dict, CFStringRef key, FFstrbuf* result); +const char* ffCfDictGetBool(CFDictionaryRef dict, CFStringRef key, bool* result); +const char* ffCfDictGetInt(CFDictionaryRef dict, CFStringRef key, int* result); + +#endif diff --git a/src/util/apple/cfdict_helpers.c b/src/util/apple/cfdict_helpers.c deleted file mode 100644 index 68ee0642b..000000000 --- a/src/util/apple/cfdict_helpers.c +++ /dev/null @@ -1,57 +0,0 @@ -#include "cfdict_helpers.h" - -bool ffCfDictGetString(CFDictionaryRef dict, CFStringRef key, FFstrbuf* result) -{ - CFTypeRef cf = (CFTypeRef)CFDictionaryGetValue(dict, key); - if(cf == NULL) - return false; - - if(CFGetTypeID(cf) == CFStringGetTypeID()) - { - CFStringRef cfStr = (CFStringRef)cf; - uint32_t length = (uint32_t)CFStringGetLength(cfStr); - //CFString stores UTF16 characters, therefore may require larger buffer to convert to UTF8 string - ffStrbufEnsureFree(result, length * 2); - if(CFStringGetCString(cfStr, result->chars, result->allocated, kCFStringEncodingUTF8)) - { - // CFStringGetCString ensures the buffer is NUL terminated - // https://developer.apple.com/documentation/corefoundation/1542721-cfstringgetcstring - result->length = (uint32_t) strnlen(result->chars, (uint32_t)result->allocated); - } - } - else if(CFGetTypeID(cf) == CFDataGetTypeID()) - { - CFDataRef cfData = (CFDataRef)cf; - uint32_t length = (uint32_t)CFDataGetLength(cfData); - ffStrbufEnsureFree(result, length + 1); - CFDataGetBytes(cfData, CFRangeMake(0, length), (uint8_t*)result->chars); - result->length = (uint32_t)strnlen(result->chars, length); - result->chars[result->length] = '\0'; - } - else - { - return false; - } - return true; -} - -bool ffCfDictGetBool(CFDictionaryRef dict, CFStringRef key, bool* result) -{ - CFBooleanRef cf = (CFBooleanRef)CFDictionaryGetValue(dict, key); - if(cf == NULL || CFGetTypeID(cf) != CFBooleanGetTypeID()) - return false; - - *result = CFBooleanGetValue(cf); - return true; -} - -bool ffCfDictGetInt(CFDictionaryRef dict, CFStringRef key, int* result) -{ - CFNumberRef cf = (CFNumberRef)CFDictionaryGetValue(dict, key); - if (cf == NULL || CFGetTypeID(cf) != CFNumberGetTypeID()) - return false; - - if(!CFNumberGetValue(cf, kCFNumberSInt32Type, result)) - return false; - return true; -} diff --git a/src/util/apple/cfdict_helpers.h b/src/util/apple/cfdict_helpers.h deleted file mode 100644 index c552f6382..000000000 --- a/src/util/apple/cfdict_helpers.h +++ /dev/null @@ -1,13 +0,0 @@ -#pragma once - -#ifndef FASTFETCH_INCLUDED_cfdict_helpers -#define FASTFETCH_INCLUDED_cfdict_helpers - -#include "fastfetch.h" -#include - -bool ffCfDictGetString(CFDictionaryRef dict, CFStringRef key, FFstrbuf* result); -bool ffCfDictGetBool(CFDictionaryRef dict, CFStringRef key, bool* result); -bool ffCfDictGetInt(CFDictionaryRef dict, CFStringRef key, int* result); - -#endif From 431f570f051e7271ee8f213b370ce6072b1887d9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E6=9D=8E=E9=80=9A=E6=B4=B2?= Date: Tue, 20 Sep 2022 17:19:18 +0800 Subject: [PATCH 2/4] Media: support player detection ( macOS ) --- CMakeLists.txt | 1 + src/detection/media/media_apple.m | 50 +++++++++++++++++++++++-------- 2 files changed, 39 insertions(+), 12 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index d0a3c9643..cd226a7d0 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -369,6 +369,7 @@ if(APPLE) PRIVATE "-framework OpenGL" PRIVATE "-framework OpenCL" PRIVATE "-framework Cocoa" + PRIVATE "-weak_framework MediaRemote -F /System/Library/PrivateFrameworks" ) endif() diff --git a/src/detection/media/media_apple.m b/src/detection/media/media_apple.m index 99f43ca49..4c4da1b54 100644 --- a/src/detection/media/media_apple.m +++ b/src/detection/media/media_apple.m @@ -2,27 +2,58 @@ #include "detection/media/media.h" #include "common/library.h" #include "util/apple/cf_helpers.h" +#include "util/apple/osascript.h" #import #import -void MRMediaRemoteGetNowPlayingInfo(dispatch_queue_t dispatcher, void(^callback)(_Nullable CFDictionaryRef info)); +extern void MRMediaRemoteGetNowPlayingInfo(dispatch_queue_t dispatcher, void(^callback)(_Nullable CFDictionaryRef info)) __attribute__((weak_import)); +extern void MRMediaRemoteGetNowPlayingClient(dispatch_queue_t dispatcher, void (^callback)(_Nullable id clientObj)) __attribute__((weak_import)); +extern CFStringRef MRNowPlayingClientGetBundleIdentifier(id clientObj) __attribute__((weak_import)); +extern CFStringRef MRNowPlayingClientGetParentAppBundleIdentifier(id clientObj) __attribute__((weak_import)); static const char* getMedia(FFMediaResult* result) { - FF_LIBRARY_LOAD(MediaRemote, NULL, "dlopen MediaRemote failed", "/System/Library/PrivateFrameworks/MediaRemote.framework/MediaRemote", -1); - FF_LIBRARY_LOAD_SYMBOL_MESSAGE(MediaRemote, MRMediaRemoteGetNowPlayingInfo); + #define FF_TEST_FN_EXISTANCE(fn) if (!fn) return "MediaRemote function " #fn " is not available" + FF_TEST_FN_EXISTANCE(MRMediaRemoteGetNowPlayingInfo); + FF_TEST_FN_EXISTANCE(MRMediaRemoteGetNowPlayingClient); + FF_TEST_FN_EXISTANCE(MRNowPlayingClientGetBundleIdentifier); + FF_TEST_FN_EXISTANCE(MRNowPlayingClientGetParentAppBundleIdentifier); + #undef FF_TEST_FN_EXISTANCE - dispatch_semaphore_t semaphore = dispatch_semaphore_create(0); - ffMRMediaRemoteGetNowPlayingInfo(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_HIGH, 0), ^(_Nullable CFDictionaryRef info) { + dispatch_group_t group = dispatch_group_create(); + dispatch_queue_t queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_HIGH, 0); + + dispatch_group_enter(group); + MRMediaRemoteGetNowPlayingInfo(queue, ^(_Nullable CFDictionaryRef info) { if(info != nil) { ffCfDictGetString(info, CFSTR("kMRMediaRemoteNowPlayingInfoTitle"), &result->song); ffCfDictGetString(info, CFSTR("kMRMediaRemoteNowPlayingInfoArtist"), &result->artist); ffCfDictGetString(info, CFSTR("kMRMediaRemoteNowPlayingInfoAlbum"), &result->album); } - dispatch_semaphore_signal(semaphore); + dispatch_group_leave(group); }); - dispatch_semaphore_wait(semaphore, DISPATCH_TIME_FOREVER); + + dispatch_group_enter(group); + MRMediaRemoteGetNowPlayingClient(queue, ^(_Nullable id clientObj) { + if (clientObj != nil) { + CFStringRef identifier = MRNowPlayingClientGetBundleIdentifier(clientObj); + if (identifier == nil) + identifier = MRNowPlayingClientGetParentAppBundleIdentifier(clientObj); + if (identifier != nil) + ffCfStrGetString(identifier, &result->busNameShort); + } + dispatch_group_leave(group); + }); + + dispatch_group_wait(group, DISPATCH_TIME_FOREVER); + + if(result->busNameShort.length > 0) + { + char buf[128]; + snprintf(buf, sizeof(buf), "name of app id \"%s\"", result->busNameShort.chars); + ffOsascript(buf, &result->player); + } if(result->song.length > 0) return NULL; @@ -35,9 +66,4 @@ void ffDetectMediaImpl(const FFinstance* instance, FFMediaResult* media) FF_UNUSED(instance) const char* error = getMedia(media); ffStrbufAppendS(&media->error, error); - - //TODO: proper detection - //I already set it here, because the player module expects it to be set if the error is not set - if(error == NULL) - ffStrbufAppendS(&media->player, "Media Player"); } From e60537512edec7667ebf00ba8fc9e09eeee1dae8 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E6=9D=8E=E9=80=9A=E6=B4=B2?= Date: Tue, 20 Sep 2022 17:46:01 +0800 Subject: [PATCH 3/4] Media: add playing status --- src/detection/media/media.c | 1 + src/detection/media/media.h | 1 + src/detection/media/media_apple.m | 7 +++++++ src/modules/song.c | 8 ++++++-- 4 files changed, 15 insertions(+), 2 deletions(-) diff --git a/src/detection/media/media.c b/src/detection/media/media.c index e82d3a522..d1cc6ab76 100644 --- a/src/detection/media/media.c +++ b/src/detection/media/media.c @@ -13,6 +13,7 @@ const FFMediaResult* ffDetectMedia(const FFinstance* instance) ffStrbufInit(&result.artist); ffStrbufInit(&result.album); ffStrbufInit(&result.url); + ffStrbufInit(&result.status); ffDetectMediaImpl(instance, &result); diff --git a/src/detection/media/media.h b/src/detection/media/media.h index 4c29779ac..7261ca246 100644 --- a/src/detection/media/media.h +++ b/src/detection/media/media.h @@ -14,6 +14,7 @@ typedef struct FFMediaResult FFstrbuf artist; FFstrbuf album; FFstrbuf url; + FFstrbuf status; } FFMediaResult; const FFMediaResult* ffDetectMedia(const FFinstance* instance); diff --git a/src/detection/media/media_apple.m b/src/detection/media/media_apple.m index 4c4da1b54..6db70d048 100644 --- a/src/detection/media/media_apple.m +++ b/src/detection/media/media_apple.m @@ -11,6 +11,7 @@ extern void MRMediaRemoteGetNowPlayingInfo(dispatch_queue_t dispatcher, void(^ca extern void MRMediaRemoteGetNowPlayingClient(dispatch_queue_t dispatcher, void (^callback)(_Nullable id clientObj)) __attribute__((weak_import)); extern CFStringRef MRNowPlayingClientGetBundleIdentifier(id clientObj) __attribute__((weak_import)); extern CFStringRef MRNowPlayingClientGetParentAppBundleIdentifier(id clientObj) __attribute__((weak_import)); +void MRMediaRemoteGetNowPlayingApplicationIsPlaying(dispatch_queue_t queue, void (^callback)(BOOL playing)); static const char* getMedia(FFMediaResult* result) { @@ -24,6 +25,12 @@ static const char* getMedia(FFMediaResult* result) dispatch_group_t group = dispatch_group_create(); dispatch_queue_t queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_HIGH, 0); + dispatch_group_enter(group); + MRMediaRemoteGetNowPlayingApplicationIsPlaying(queue, ^(BOOL playing) { + ffStrbufAppendS(&result->status, playing ? "Playing" : "Paused"); + dispatch_group_leave(group); + }); + dispatch_group_enter(group); MRMediaRemoteGetNowPlayingInfo(queue, ^(_Nullable CFDictionaryRef info) { if(info != nil) { diff --git a/src/modules/song.c b/src/modules/song.c index b1477020c..e4d174e5a 100644 --- a/src/modules/song.c +++ b/src/modules/song.c @@ -5,7 +5,7 @@ #include #define FF_SONG_MODULE_NAME "Media" -#define FF_SONG_NUM_FORMAT_ARGS 4 +#define FF_SONG_NUM_FORMAT_ARGS 5 static bool shouldIgoreChar(char c) { @@ -89,6 +89,9 @@ void ffPrintSong(FFinstance* instance) fputs(" - ", stdout); } + if (media->status.length > 0) + ffStrbufAppendF(&songPretty, " (%s)", media->status.chars); + ffStrbufPutTo(&songPretty, stdout); } else @@ -97,7 +100,8 @@ void ffPrintSong(FFinstance* instance) {FF_FORMAT_ARG_TYPE_STRBUF, &songPretty}, {FF_FORMAT_ARG_TYPE_STRBUF, &media->song}, {FF_FORMAT_ARG_TYPE_STRBUF, &media->artist}, - {FF_FORMAT_ARG_TYPE_STRBUF, &media->album} + {FF_FORMAT_ARG_TYPE_STRBUF, &media->album}, + {FF_FORMAT_ARG_TYPE_STRBUF, &media->status} }); } From 7f19cb1d2175dbe38249c05628a5aaaa3b0cffa8 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E6=9D=8E=E9=80=9A=E6=B4=B2?= Date: Tue, 20 Sep 2022 18:26:50 +0800 Subject: [PATCH 4/4] Battery: print error info instead of nan% ( macOS ) --- src/detection/battery/battery_apple.c | 16 +++++++++++++--- 1 file changed, 13 insertions(+), 3 deletions(-) diff --git a/src/detection/battery/battery_apple.c b/src/detection/battery/battery_apple.c index 979367662..8fa956c77 100644 --- a/src/detection/battery/battery_apple.c +++ b/src/detection/battery/battery_apple.c @@ -27,13 +27,23 @@ const char* ffDetectBatteryImpl(FFinstance* instance, FFlist* results) } bool boolValue; + const char* error; BatteryResult* battery = ffListAdd(results); ffStrbufInit(&battery->capacity); int currentCapacity, maxCapacity; - if(!ffCfDictGetInt(properties, CFSTR("CurrentCapacity"), ¤tCapacity) && - !ffCfDictGetInt(properties, CFSTR("MaxCapacity"), &maxCapacity)) - ffStrbufAppendF(&battery->capacity, "%.0f", currentCapacity * 100.0 / maxCapacity); + + if ((error = ffCfDictGetInt(properties, CFSTR("MaxCapacity"), &maxCapacity))) + return error; + if (maxCapacity <= 0) + return "Querying MaxCapacity failed"; + + if ((error = ffCfDictGetInt(properties, CFSTR("CurrentCapacity"), ¤tCapacity))) + return error; + if(currentCapacity <= 0) + return "Querying CurrentCapacity failed"; + + ffStrbufAppendF(&battery->capacity, "%.0f", currentCapacity * 100.0 / maxCapacity); ffStrbufInit(&battery->manufacturer); ffStrbufInit(&battery->modelName);