mirror of
https://github.com/fastfetch-cli/fastfetch.git
synced 2026-09-12 10:22:12 +02:00
Merge pull request #245 from CarterLi/master
[macOS] add player detection
This commit is contained in:
+2
-1
@@ -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
|
||||
@@ -369,6 +369,7 @@ if(APPLE)
|
||||
PRIVATE "-framework OpenGL"
|
||||
PRIVATE "-framework OpenCL"
|
||||
PRIVATE "-framework Cocoa"
|
||||
PRIVATE "-weak_framework MediaRemote -F /System/Library/PrivateFrameworks"
|
||||
)
|
||||
endif()
|
||||
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
#include "fastfetch.h"
|
||||
#include "battery.h"
|
||||
#include "util/apple/cfdict_helpers.h"
|
||||
#include "util/apple/cf_helpers.h"
|
||||
|
||||
#include <IOKit/IOKitLib.h>
|
||||
|
||||
@@ -27,18 +27,28 @@ 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);
|
||||
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 +62,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, "");
|
||||
|
||||
@@ -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 <IOKit/graphics/IOGraphicsLib.h>
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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);
|
||||
|
||||
|
||||
@@ -14,6 +14,7 @@ typedef struct FFMediaResult
|
||||
FFstrbuf artist;
|
||||
FFstrbuf album;
|
||||
FFstrbuf url;
|
||||
FFstrbuf status;
|
||||
} FFMediaResult;
|
||||
|
||||
const FFMediaResult* ffDetectMedia(const FFinstance* instance);
|
||||
|
||||
@@ -1,28 +1,66 @@
|
||||
#include "fastfetch.h"
|
||||
#include "detection/media/media.h"
|
||||
#include "common/library.h"
|
||||
#include "util/apple/cfdict_helpers.h"
|
||||
#include "util/apple/cf_helpers.h"
|
||||
#include "util/apple/osascript.h"
|
||||
|
||||
#import <Foundation/Foundation.h>
|
||||
#import <CoreFoundation/CoreFoundation.h>
|
||||
|
||||
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));
|
||||
void MRMediaRemoteGetNowPlayingApplicationIsPlaying(dispatch_queue_t queue, void (^callback)(BOOL playing));
|
||||
|
||||
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);
|
||||
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) {
|
||||
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 +73,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");
|
||||
}
|
||||
|
||||
+6
-2
@@ -5,7 +5,7 @@
|
||||
#include <ctype.h>
|
||||
|
||||
#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}
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
@@ -0,0 +1,15 @@
|
||||
#pragma once
|
||||
|
||||
#ifndef FASTFETCH_INCLUDED_cf_helpers
|
||||
#define FASTFETCH_INCLUDED_cf_helpers
|
||||
|
||||
#include "fastfetch.h"
|
||||
#include <CoreFoundation/CoreFoundation.h>
|
||||
|
||||
//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
|
||||
@@ -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;
|
||||
}
|
||||
@@ -1,13 +0,0 @@
|
||||
#pragma once
|
||||
|
||||
#ifndef FASTFETCH_INCLUDED_cfdict_helpers
|
||||
#define FASTFETCH_INCLUDED_cfdict_helpers
|
||||
|
||||
#include "fastfetch.h"
|
||||
#include <CoreFoundation/CoreFoundation.h>
|
||||
|
||||
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
|
||||
Reference in New Issue
Block a user