5 * Created by Evan Schoenberg on 3/27/06.
9 #include "PowerNotifier.h"
10 #include <IOKit/IOKitLib.h>
11 #include <IOKit/ps/IOPSKeys.h>
12 #include <IOKit/ps/IOPowerSources.h>
14 #include "AppController.h"
16 extern void NSLog(CFStringRef format, ...);
18 static CFRunLoopSourceRef powerNotifierRunLoopSource = NULL;
19 static HGPowerSource lastPowerSource;
20 static CFBooleanRef lastChargingState;
21 static int lastBatteryTime = -1;
23 static bool stringsAreEqual(CFStringRef a, CFStringRef b)
25 if (!a || !b) return 0;
27 return (CFStringCompare(a, b, 0) == kCFCompareEqualTo);
30 static void powerSourceChanged(void *context)
32 #pragma unused(context)
33 CFTypeRef powerBlob = IOPSCopyPowerSourcesInfo();
34 CFArrayRef powerSourcesList = IOPSCopyPowerSourcesList(powerBlob);
36 unsigned count = CFArrayGetCount(powerSourcesList);
37 for (unsigned i = 0U; i < count; ++i) {
38 CFTypeRef powerSource;
39 CFDictionaryRef description;
41 HGPowerSource hgPowerSource;
42 CFBooleanRef charging = kCFBooleanFalse;
44 int percentageCapacity = -1;
46 powerSource = CFArrayGetValueAtIndex(powerSourcesList, i);
47 description = IOPSGetPowerSourceDescription(powerBlob, powerSource);
49 //Don't display anything for power sources that aren't present (i.e. an absent second battery in a 2-battery machine)
50 if (CFDictionaryGetValue(description, CFSTR(kIOPSIsPresentKey)) == kCFBooleanFalse)
53 //We only know how to handle internal (battery, a/c power) transport types. The other values indicate UPS usage.
54 if (stringsAreEqual(CFDictionaryGetValue(description, CFSTR(kIOPSTransportTypeKey)),
55 CFSTR(kIOPSInternalType))) {
56 CFStringRef currentState = CFDictionaryGetValue(description, CFSTR(kIOPSPowerSourceStateKey));
57 if (stringsAreEqual(currentState, CFSTR(kIOPSACPowerValue)))
58 hgPowerSource = HGACPower;
59 else if (stringsAreEqual(currentState, CFSTR(kIOPSBatteryPowerValue)))
60 hgPowerSource = HGBatteryPower;
62 hgPowerSource = HGUnknownPower;
65 if (CFDictionaryGetValue(description, CFSTR(kIOPSIsChargingKey)) == kCFBooleanTrue) {
67 charging = kCFBooleanTrue;
69 CFNumberRef timeToChargeNum = CFDictionaryGetValue(description, CFSTR(kIOPSTimeToFullChargeKey));
72 if (CFNumberGetValue(timeToChargeNum, kCFNumberIntType, &timeToCharge))
73 batteryTime = timeToCharge;
76 charging = kCFBooleanFalse;
78 CFNumberRef timeToEmptyNum = CFDictionaryGetValue(description, CFSTR(kIOPSTimeToEmptyKey));
81 if (CFNumberGetValue(timeToEmptyNum, kCFNumberIntType, &timeToEmpty))
82 batteryTime = timeToEmpty;
86 CFNumberRef currentCapacityNum = CFDictionaryGetValue(description, CFSTR(kIOPSCurrentCapacityKey));
87 CFNumberRef maxCapacityNum = CFDictionaryGetValue(description, CFSTR(kIOPSMaxCapacityKey));
89 int currentCapacity, maxCapacity;
91 if (CFNumberGetValue(currentCapacityNum, kCFNumberIntType, ¤tCapacity) &&
92 CFNumberGetValue(maxCapacityNum, kCFNumberIntType, &maxCapacity))
93 percentageCapacity = roundf((currentCapacity / (float)maxCapacity) * 100.0f);
97 hgPowerSource = HGUPSPower;
100 //Avoid sending notifications on the same power source multiple times, unless the charging state or presence/absence of a time estimate has changed.
101 if (lastPowerSource != hgPowerSource || lastChargingState != charging || (lastBatteryTime == -1) != (batteryTime == -1)) {
102 lastPowerSource = hgPowerSource;
103 lastChargingState = charging;
104 lastBatteryTime = batteryTime;
105 AppController_powerSwitched(hgPowerSource, charging, batteryTime, percentageCapacity);
109 CFRelease(powerSourcesList);
110 CFRelease(powerBlob);
113 void PowerNotifier_init(void)
115 powerNotifierRunLoopSource = IOPSNotificationCreateRunLoopSource(powerSourceChanged,
117 if (powerNotifierRunLoopSource)
118 CFRunLoopAddSource(CFRunLoopGetCurrent(), powerNotifierRunLoopSource, kCFRunLoopDefaultMode);
120 lastPowerSource = HGUnknownPower;
123 void PowerNotifier_dealloc(void)
125 CFRunLoopRemoveSource(CFRunLoopGetCurrent(), powerNotifierRunLoopSource, kCFRunLoopDefaultMode);
126 CFRelease(powerNotifierRunLoopSource);