The current implementation based on `IOPMCopyBatteryInfo` does not work (macOS 13.2, M2 Max, returns `kIOReturnUnsupported`) and is not recommended/supported by Apple:
WARNING! IOPMCoyBatteryInfo is unsupported on ALL Intel CPU based systems. For PPC CPU based systems, it remains not recommended. For almost all purposes, developers should use the richer IOPowerSources API (with change notifications) instead of using IOPMCopyBatteryInfo. Keys to decipher IOPMCopyBatteryInfo's return CFArray exist in IOPM.h.
-- v2: Apply 1 suggestion(s) to 1 file(s)
From: Marc-Aurel Zent marc_aurel@me.com
--- dlls/ntdll/unix/system.c | 87 ++++++++++++++++++++++++++-------------- 1 file changed, 56 insertions(+), 31 deletions(-)
diff --git a/dlls/ntdll/unix/system.c b/dlls/ntdll/unix/system.c index 33e93bbdf32..6e4eb14b0ef 100644 --- a/dlls/ntdll/unix/system.c +++ b/dlls/ntdll/unix/system.c @@ -57,8 +57,7 @@ #ifdef __APPLE__ # include <CoreFoundation/CoreFoundation.h> # include <IOKit/IOKitLib.h> -# include <IOKit/pwr_mgt/IOPM.h> -# include <IOKit/pwr_mgt/IOPMLib.h> +# include <IOKit/ps/IOPSKeys.h> # include <IOKit/ps/IOPowerSources.h> # include <mach/mach.h> # include <mach/machine.h> @@ -3494,66 +3493,92 @@ static NTSTATUS fill_battery_state( SYSTEM_BATTERY_STATE *bs )
static NTSTATUS fill_battery_state( SYSTEM_BATTERY_STATE *bs ) { - CFArrayRef batteries; - CFDictionaryRef battery; - CFNumberRef prop; - uint32_t value, voltage; - CFTimeInterval remain; + CFTypeRef blob = IOPSCopyPowerSourcesInfo(); + CFArrayRef sources = IOPSCopyPowerSourcesList( blob ); + CFDictionaryRef source = NULL; + const void *prop; + const char *power_source_state, *source_type; + Boolean is_charging; + int32_t value, voltage;
- if (IOPMCopyBatteryInfo( 0, &batteries ) != kIOReturnSuccess) - return STATUS_ACCESS_DENIED; - - if (CFArrayGetCount( batteries ) == 0) + if (CFArrayGetCount( sources ) == 0) { /* Just assume we're on AC with no battery. */ bs->AcOnLine = TRUE; return STATUS_SUCCESS; } /* Just use the first battery. */ - battery = CFArrayGetValueAtIndex( batteries, 0 ); - - prop = CFDictionaryGetValue( battery, CFSTR(kIOBatteryFlagsKey) ); - CFNumberGetValue( prop, kCFNumberSInt32Type, &value ); + source = IOPSGetPowerSourceDescription( blob, CFArrayGetValueAtIndex( sources, 0 ) ); + + if (!source) + return STATUS_ACCESS_DENIED;
- if (value & kIOBatteryInstalled) + prop = CFDictionaryGetValue( source, CFSTR(kIOPSTypeKey) ); + source_type = CFStringGetCStringPtr((CFStringRef) prop, kCFStringEncodingASCII); + + if (strstr(source_type, "Battery")) bs->BatteryPresent = TRUE; else /* Since we are executing code, we must have AC power. */ bs->AcOnLine = TRUE; - if (value & kIOBatteryChargerConnect) + + prop = CFDictionaryGetValue( source, CFSTR(kIOPSPowerSourceStateKey) ); + power_source_state = CFStringGetCStringPtr( (CFStringRef)prop, kCFStringEncodingASCII ); + + prop = CFDictionaryGetValue( source, CFSTR(kIOPSIsChargingKey) ); + is_charging = CFBooleanGetValue( (CFBooleanRef)prop ); + + if (!power_source_state || strcmp(power_source_state, "Battery Power")) { bs->AcOnLine = TRUE; - if (value & kIOBatteryCharge) + if (is_charging) bs->Charging = TRUE; } else bs->Discharging = TRUE;
/* We'll need the voltage to be able to interpret the other values. */ - prop = CFDictionaryGetValue( battery, CFSTR(kIOBatteryVoltageKey) ); - CFNumberGetValue( prop, kCFNumberSInt32Type, &voltage ); + prop = CFDictionaryGetValue( source, CFSTR(kIOPSVoltageKey) ); + if (prop) + CFNumberGetValue( (CFNumberRef)prop, kCFNumberSInt32Type, &voltage ); + else + /* kIOPSCurrentKey is optional and might not be populated. + * Assume 11.4 V then, which is a common value for Apple laptops. */ + voltage = 11400;
- prop = CFDictionaryGetValue( battery, CFSTR(kIOBatteryCapacityKey) ); - CFNumberGetValue( prop, kCFNumberSInt32Type, &value ); + prop = CFDictionaryGetValue( source, CFSTR(kIOPSMaxCapacityKey) ); + CFNumberGetValue( (CFNumberRef)prop, kCFNumberSInt32Type, &value ); bs->MaxCapacity = value * voltage; /* Apple uses "estimated time < 10:00" and "22%" for these, but we'll follow * Windows for now (5% and 33%). */ bs->DefaultAlert1 = bs->MaxCapacity / 20; bs->DefaultAlert2 = bs->MaxCapacity / 3;
- prop = CFDictionaryGetValue( battery, CFSTR(kIOBatteryCurrentChargeKey) ); - CFNumberGetValue( prop, kCFNumberSInt32Type, &value ); + prop = CFDictionaryGetValue( source, CFSTR(kIOPSCurrentCapacityKey) ); + CFNumberGetValue( (CFNumberRef)prop, kCFNumberSInt32Type, &value ); bs->RemainingCapacity = value * voltage;
- prop = CFDictionaryGetValue( battery, CFSTR(kIOBatteryAmperageKey) ); - CFNumberGetValue( prop, kCFNumberSInt32Type, &value ); - bs->Rate = value * voltage; + prop = CFDictionaryGetValue( source, CFSTR(kIOPSCurrentKey) ); + if (prop) + CFNumberGetValue( (CFNumberRef)prop, kCFNumberSInt32Type, &value ); + else + /* kIOPSCurrentKey is optional and might not be populated. */ + value = 0; + + bs->Rate = value * voltage / 1000;
- remain = IOPSGetTimeRemainingEstimate(); - if (remain != kIOPSTimeRemainingUnknown && remain != kIOPSTimeRemainingUnlimited) - bs->EstimatedTime = (ULONG)remain; + prop = CFDictionaryGetValue( source, CFSTR(kIOPSTimeToEmptyKey) ); + if (prop) + { + CFNumberGetValue( (CFNumberRef)prop, kCFNumberSInt32Type, &value ); + if (value > 0) + /* A value of -1 indicates "Still Calculating the Time", + * otherwise estimated minutes left on the battery. */ + bs->EstimatedTime = (ULONG)value * 60; + }
- CFRelease( batteries ); + CFRelease( blob ); + CFRelease( sources ); return STATUS_SUCCESS; }
From: Chip Davis cdavis5x@gmail.com
--- dlls/ntdll/unix/system.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/dlls/ntdll/unix/system.c b/dlls/ntdll/unix/system.c index 6e4eb14b0ef..7dd377b0f36 100644 --- a/dlls/ntdll/unix/system.c +++ b/dlls/ntdll/unix/system.c @@ -3542,7 +3542,7 @@ static NTSTATUS fill_battery_state( SYSTEM_BATTERY_STATE *bs ) if (prop) CFNumberGetValue( (CFNumberRef)prop, kCFNumberSInt32Type, &voltage ); else - /* kIOPSCurrentKey is optional and might not be populated. + /* kIOPSVoltageKey is optional and might not be populated. * Assume 11.4 V then, which is a common value for Apple laptops. */ voltage = 11400;