diff --git a/CMakeLists.txt b/CMakeLists.txt index 81c703e5d..98f83b3a8 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -243,6 +243,7 @@ if(APPLE) src/detection/battery/battery_apple.c src/detection/memory/memory_apple.c src/detection/displayserver/displayserver_apple.c + src/util/apple/cfdict_helpers.c ) elseif(ANDROID) list(APPEND LIBFASTFETCH_SRC diff --git a/src/detection/battery/battery_apple.c b/src/detection/battery/battery_apple.c index fa056c568..53a2431e5 100644 --- a/src/detection/battery/battery_apple.c +++ b/src/detection/battery/battery_apple.c @@ -1,7 +1,68 @@ #include "fastfetch.h" #include "battery.h" +#include "util/apple/cfdict_helpers.h" -const char* ffDetectBatteryImpl(FFinstance* instance, FFlist* results) { - FF_UNUSED(instance, results); - return "Unimplemented"; +#include + +const char* ffDetectBatteryImpl(FFinstance* instance, FFlist* results) +{ + FF_UNUSED(instance); + + CFMutableDictionaryRef matchDict = IOServiceMatching("AppleSmartBattery"); + if (matchDict == NULL) + return "IOServiceMatching(\"AppleSmartBattery\") failed"; + + io_iterator_t iterator; + if(IOServiceGetMatchingServices(0, matchDict, &iterator) != kIOReturnSuccess) + return "IOServiceGetMatchingServices() failed"; + + io_registry_entry_t registryEntry; + while((registryEntry = IOIteratorNext(iterator)) != 0) + { + CFMutableDictionaryRef properties; + if(IORegistryEntryCreateCFProperties(registryEntry, &properties, kCFAllocatorDefault, kNilOptions) != kIOReturnSuccess) + { + IOObjectRelease(registryEntry); + continue; + } + + int intValue; + bool boolValue; + + BatteryResult* battery = ffListAdd(results); + ffStrbufInit(&battery->capacity); + if(ffCfDictGetInt(properties, "CurrentCapacity", &intValue)) + ffStrbufAppendF(&battery->capacity, "%d", intValue); + + ffStrbufInit(&battery->manufacturer); + ffStrbufInit(&battery->modelName); + ffStrbufInit(&battery->technology); + if (ffCfDictGetBool(properties, "built-in", &boolValue) && boolValue) + { + ffStrbufAppendS(&battery->manufacturer, "Apple Inc."); + ffStrbufAppendS(&battery->modelName, "Builtin"); + ffStrbufAppendS(&battery->technology, "Lithium"); + } + else + { + ffStrbufAppendS(&battery->manufacturer, "Unknown"); + ffStrbufAppendS(&battery->modelName, "Unknown"); + ffStrbufAppendS(&battery->technology, "Unknown"); + } + + ffStrbufInit(&battery->status); + if (ffCfDictGetBool(properties, "FullyCharged", &boolValue) && boolValue) + ffStrbufAppendS(&battery->status, "Fully charged"); + else if (ffCfDictGetBool(properties, "IsCharging", &boolValue) && boolValue) + ffStrbufAppendS(&battery->status, "Charging"); + else + ffStrbufAppendS(&battery->status, ""); + + CFRelease(properties); + IOObjectRelease(registryEntry); + } + + IOObjectRelease(iterator); + + return NULL; } diff --git a/src/util/apple/cfdict_helpers.c b/src/util/apple/cfdict_helpers.c new file mode 100644 index 000000000..9da7ca917 --- /dev/null +++ b/src/util/apple/cfdict_helpers.c @@ -0,0 +1,45 @@ +#include "cfdict_helpers.h" + +const void* ffCfDictGetValue(CFMutableDictionaryRef dict, const char* key) +{ + CFStringRef cfKey = CFStringCreateWithCStringNoCopy(NULL, key, kCFStringEncodingASCII, kCFAllocatorNull); + return CFDictionaryGetValue(dict, cfKey); +} + +bool ffCfDictGetString(CFMutableDictionaryRef dict, const char* key, FFstrbuf* result) +{ + CFStringRef cf = (CFStringRef)ffCfDictGetValue(dict, key); + if(cf == NULL || CFGetTypeID(cf) != CFStringGetTypeID()) + return false; + + uint32_t length = (uint32_t)CFStringGetLength(cf); + ffStrbufEnsureFree(result, length + 1); + if(CFStringGetCString(cf, result->chars, length + 1, kCFStringEncodingASCII)) + { + result->length = length; + // CFStringGetCString ensures the buffer is NUL terminated + // https://developer.apple.com/documentation/corefoundation/1542721-cfstringgetcstring + } + return true; +} + +bool ffCfDictGetBool(CFMutableDictionaryRef dict, const char* key, bool* result) +{ + CFBooleanRef cf = (CFBooleanRef)ffCfDictGetValue(dict, key); + if(cf == NULL || CFGetTypeID(cf) != CFNumberGetTypeID()) + return false; + + *result = CFBooleanGetValue(cf); + return true; +} + +bool ffCfDictGetInt(CFMutableDictionaryRef dict, const char* key, int* result) +{ + CFNumberRef cf = (CFNumberRef)ffCfDictGetValue(dict, key); + if (cf == NULL || CFGetTypeID(cf) != CFStringGetTypeID()) + 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 new file mode 100644 index 000000000..a00522e74 --- /dev/null +++ b/src/util/apple/cfdict_helpers.h @@ -0,0 +1,14 @@ +#pragma once + +#ifndef FASTFETCH_INCLUDED_cfdict_helpers +#define FASTFETCH_INCLUDED_cfdict_helpers + +#include "fastfetch.h" +#include + +const void* ffCfDictGetValue(CFMutableDictionaryRef dict, const char* str); +bool ffCfDictGetString(CFMutableDictionaryRef dict, const char* key, FFstrbuf* result); +bool ffCfDictGetBool(CFMutableDictionaryRef dict, const char* key, bool* result); +bool ffCfDictGetInt(CFMutableDictionaryRef dict, const char* key, int* result); + +#endif