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.
-- v13: ntdll: Use IOPowerSources API to fill battery info on macOS
From: Marc-Aurel Zent marc_aurel@me.com
Apply 1 suggestion(s) to 1 file(s)
remove unnecessary CFTypeRef casts
handle errors is IOPSCopyPowerSourcesList
always release resources
use CFStringCompare --- dlls/ntdll/unix/system.c | 107 ++++++++++++++++++++++++++------------- 1 file changed, 73 insertions(+), 34 deletions(-)
diff --git a/dlls/ntdll/unix/system.c b/dlls/ntdll/unix/system.c index 07598c8c95e..32ca510db4e 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> @@ -3580,66 +3579,106 @@ 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 ); + CFIndex count, i; + CFDictionaryRef source = NULL; + CFTypeRef prop; + Boolean is_charging, is_internal, is_present; + int32_t value, voltage;
- if (IOPMCopyBatteryInfo( 0, &batteries ) != kIOReturnSuccess) + if (!sources) + { + if (blob) CFRelease( blob ); return STATUS_ACCESS_DENIED; + } + + count = CFArrayGetCount( sources ); + + for (i = 0; i < count; i++) + { + source = IOPSGetPowerSourceDescription( blob, CFArrayGetValueAtIndex( sources, i ) ); + + if (!source) + continue; + + prop = CFDictionaryGetValue( source, CFSTR(kIOPSTransportTypeKey) ); + is_internal = !CFStringCompare( prop, CFSTR(kIOPSInternalType), 0 ); + + prop = CFDictionaryGetValue( source, CFSTR(kIOPSIsPresentKey) ); + is_present = CFBooleanGetValue( prop ); + + if (is_internal && is_present) + break; + }
- if (CFArrayGetCount( batteries ) == 0) + CFRelease( blob ); + + if (!source) { - /* Just assume we're on AC with no battery. */ + /* Just assume we're on AC with no internal power source. */ bs->AcOnLine = TRUE; + CFRelease( sources ); return STATUS_SUCCESS; } - /* Just use the first battery. */ - battery = CFArrayGetValueAtIndex( batteries, 0 );
- prop = CFDictionaryGetValue( battery, CFSTR(kIOBatteryFlagsKey) ); - CFNumberGetValue( prop, kCFNumberSInt32Type, &value ); + bs->BatteryPresent = TRUE;
- if (value & kIOBatteryInstalled) - bs->BatteryPresent = TRUE; - else - /* Since we are executing code, we must have AC power. */ - bs->AcOnLine = TRUE; - if (value & kIOBatteryChargerConnect) + prop = CFDictionaryGetValue( source, CFSTR(kIOPSIsChargingKey) ); + is_charging = CFBooleanGetValue( prop ); + + prop = CFDictionaryGetValue( source, CFSTR(kIOPSPowerSourceStateKey) ); + + if (!CFStringCompare( prop, CFSTR(kIOPSACPowerValue), 0 )) { 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( prop, kCFNumberIntType, &voltage ); + else + /* kIOPSVoltageKey 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( prop, kCFNumberIntType, &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( prop, kCFNumberIntType, &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( prop, kCFNumberIntType, &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( prop, kCFNumberIntType, &value ); + if (value > 0) + /* A value of -1 indicates "Still Calculating the Time", + * otherwise estimated minutes left on the battery. */ + bs->EstimatedTime = value * 60; + }
- CFRelease( batteries ); + CFRelease( sources ); return STATUS_SUCCESS; }
Hi,
It looks like your patch introduced the new failures shown below. Please investigate and fix them before resubmitting your patch. If they are not new, fixing them anyway would help a lot. Otherwise please ask for the known failures list to be updated.
The tests also ran into some preexisting test failures. If you know how to fix them that would be helpful. See the TestBot job for the details:
The full results can be found at: https://testbot.winehq.org/JobDetails.pl?Key=139755
Your paranoid android.
=== debian11b (64 bit WoW report) ===
winmm: midi.c:926: Test failed: expected 12 ticks, got 15
@bshanks rebased and tested it just now, should still be good as is
On Sat Nov 18 00:34:31 2023 +0000, Marc-Aurel Zent wrote:
@bshanks rebased and tested it just now, should still be good as is
Thanks, I gave it a try also, looks good to me