|
evands@3121
|
1 |
/*
|
|
evands@3121
|
2 |
* PowerNotifier.c
|
|
evands@3121
|
3 |
* HardwareGrowler
|
|
evands@3121
|
4 |
*
|
|
evands@3121
|
5 |
* Created by Evan Schoenberg on 3/27/06.
|
|
evands@3121
|
6 |
*
|
|
evands@3121
|
7 |
*/
|
|
evands@3121
|
8 |
|
|
evands@3121
|
9 |
#include "PowerNotifier.h"
|
|
evands@3121
|
10 |
#include <IOKit/IOKitLib.h>
|
|
evands@3121
|
11 |
#include <IOKit/ps/IOPSKeys.h>
|
|
evands@3121
|
12 |
#include <IOKit/ps/IOPowerSources.h>
|
|
evands@3121
|
13 |
|
|
evands@3121
|
14 |
#include "AppController.h"
|
|
evands@3121
|
15 |
|
|
evands@3121
|
16 |
extern void NSLog(CFStringRef format, ...);
|
|
evands@3121
|
17 |
|
|
evands@3121
|
18 |
static CFRunLoopSourceRef powerNotifierRunLoopSource = NULL;
|
|
evands@3121
|
19 |
static HGPowerSource lastPowerSource;
|
|
Peter@4205
|
20 |
static CFBooleanRef lastChargingState;
|
|
Peter@4205
|
21 |
static int lastBatteryTime = -1;
|
|
evands@3121
|
22 |
|
|
boredzo@3308
|
23 |
static bool stringsAreEqual(CFStringRef a, CFStringRef b)
|
|
evands@3121
|
24 |
{
|
|
evands@3121
|
25 |
if (!a || !b) return 0;
|
|
ingmarstein@3125
|
26 |
|
|
evands@3121
|
27 |
return (CFStringCompare(a, b, 0) == kCFCompareEqualTo);
|
|
evands@3121
|
28 |
}
|
|
evands@3121
|
29 |
|
|
evands@3121
|
30 |
static void powerSourceChanged(void *context)
|
|
evands@3121
|
31 |
{
|
|
evands@3121
|
32 |
#pragma unused(context)
|
|
ingmarstein@3125
|
33 |
CFTypeRef powerBlob = IOPSCopyPowerSourcesInfo();
|
|
ingmarstein@3125
|
34 |
CFArrayRef powerSourcesList = IOPSCopyPowerSourcesList(powerBlob);
|
|
evands@3121
|
35 |
|
|
ingmarstein@3125
|
36 |
unsigned count = CFArrayGetCount(powerSourcesList);
|
|
ingmarstein@3125
|
37 |
for (unsigned i = 0U; i < count; ++i) {
|
|
evands@3121
|
38 |
CFTypeRef powerSource;
|
|
evands@3121
|
39 |
CFDictionaryRef description;
|
|
evands@3121
|
40 |
|
|
evands@3121
|
41 |
HGPowerSource hgPowerSource;
|
|
evands@3121
|
42 |
CFBooleanRef charging = kCFBooleanFalse;
|
|
evands@3121
|
43 |
int batteryTime = -1;
|
|
evands@3121
|
44 |
int percentageCapacity = -1;
|
|
ingmarstein@3125
|
45 |
|
|
evands@3121
|
46 |
powerSource = CFArrayGetValueAtIndex(powerSourcesList, i);
|
|
evands@3121
|
47 |
description = IOPSGetPowerSourceDescription(powerBlob, powerSource);
|
|
evands@3121
|
48 |
|
|
evands@3121
|
49 |
//Don't display anything for power sources that aren't present (i.e. an absent second battery in a 2-battery machine)
|
|
evands@3121
|
50 |
if (CFDictionaryGetValue(description, CFSTR(kIOPSIsPresentKey)) == kCFBooleanFalse)
|
|
evands@3121
|
51 |
continue;
|
|
evands@3121
|
52 |
|
|
ingmarstein@3186
|
53 |
//We only know how to handle internal (battery, a/c power) transport types. The other values indicate UPS usage.
|
|
evands@3121
|
54 |
if (stringsAreEqual(CFDictionaryGetValue(description, CFSTR(kIOPSTransportTypeKey)),
|
|
evands@3121
|
55 |
CFSTR(kIOPSInternalType))) {
|
|
evands@3121
|
56 |
CFStringRef currentState = CFDictionaryGetValue(description, CFSTR(kIOPSPowerSourceStateKey));
|
|
ingmarstein@3125
|
57 |
if (stringsAreEqual(currentState, CFSTR(kIOPSACPowerValue)))
|
|
evands@3121
|
58 |
hgPowerSource = HGACPower;
|
|
ingmarstein@3125
|
59 |
else if (stringsAreEqual(currentState, CFSTR(kIOPSBatteryPowerValue)))
|
|
evands@3121
|
60 |
hgPowerSource = HGBatteryPower;
|
|
ingmarstein@3125
|
61 |
else
|
|
evands@3121
|
62 |
hgPowerSource = HGUnknownPower;
|
|
evands@3121
|
63 |
|
|
evands@3121
|
64 |
//Battery power
|
|
evands@3121
|
65 |
if (CFDictionaryGetValue(description, CFSTR(kIOPSIsChargingKey)) == kCFBooleanTrue) {
|
|
evands@3121
|
66 |
//Charging
|
|
evands@3121
|
67 |
charging = kCFBooleanTrue;
|
|
ingmarstein@3125
|
68 |
|
|
evands@3121
|
69 |
CFNumberRef timeToChargeNum = CFDictionaryGetValue(description, CFSTR(kIOPSTimeToFullChargeKey));
|
|
evands@3121
|
70 |
int timeToCharge;
|
|
ingmarstein@3125
|
71 |
|
|
ingmarstein@3125
|
72 |
if (CFNumberGetValue(timeToChargeNum, kCFNumberIntType, &timeToCharge))
|
|
evands@3121
|
73 |
batteryTime = timeToCharge;
|
|
evands@3121
|
74 |
} else {
|
|
evands@3121
|
75 |
//Not charging
|
|
evands@3121
|
76 |
charging = kCFBooleanFalse;
|
|
ingmarstein@3125
|
77 |
|
|
evands@3121
|
78 |
CFNumberRef timeToEmptyNum = CFDictionaryGetValue(description, CFSTR(kIOPSTimeToEmptyKey));
|
|
evands@3121
|
79 |
int timeToEmpty;
|
|
ingmarstein@3125
|
80 |
|
|
ingmarstein@3125
|
81 |
if (CFNumberGetValue(timeToEmptyNum, kCFNumberIntType, &timeToEmpty))
|
|
evands@3121
|
82 |
batteryTime = timeToEmpty;
|
|
evands@3121
|
83 |
}
|
|
ingmarstein@3125
|
84 |
|
|
evands@3121
|
85 |
/* Capacity */
|
|
evands@3121
|
86 |
CFNumberRef currentCapacityNum = CFDictionaryGetValue(description, CFSTR(kIOPSCurrentCapacityKey));
|
|
evands@3121
|
87 |
CFNumberRef maxCapacityNum = CFDictionaryGetValue(description, CFSTR(kIOPSMaxCapacityKey));
|
|
ingmarstein@3125
|
88 |
|
|
evands@3121
|
89 |
int currentCapacity, maxCapacity;
|
|
ingmarstein@3125
|
90 |
|
|
evands@3121
|
91 |
if (CFNumberGetValue(currentCapacityNum, kCFNumberIntType, ¤tCapacity) &&
|
|
ingmarstein@3125
|
92 |
CFNumberGetValue(maxCapacityNum, kCFNumberIntType, &maxCapacity))
|
|
ingmarstein@3125
|
93 |
percentageCapacity = roundf((currentCapacity / (float)maxCapacity) * 100.0f);
|
|
ingmarstein@3125
|
94 |
|
|
evands@3121
|
95 |
} else {
|
|
evands@3121
|
96 |
//UPS power
|
|
evands@3121
|
97 |
hgPowerSource = HGUPSPower;
|
|
evands@3121
|
98 |
}
|
|
ingmarstein@3125
|
99 |
|
|
Peter@4205
|
100 |
//Avoid sending notifications on the same power source multiple times, unless the charging state or presence/absence of a time estimate has changed.
|
|
Peter@4205
|
101 |
if (lastPowerSource != hgPowerSource || lastChargingState != charging || (lastBatteryTime == -1) != (batteryTime == -1)) {
|
|
evands@3121
|
102 |
lastPowerSource = hgPowerSource;
|
|
Peter@4205
|
103 |
lastChargingState = charging;
|
|
Peter@4205
|
104 |
lastBatteryTime = batteryTime;
|
|
evands@3121
|
105 |
AppController_powerSwitched(hgPowerSource, charging, batteryTime, percentageCapacity);
|
|
evands@3121
|
106 |
}
|
|
evands@3121
|
107 |
}
|
|
evands@3121
|
108 |
|
|
evands@3121
|
109 |
CFRelease(powerSourcesList);
|
|
evands@3121
|
110 |
CFRelease(powerBlob);
|
|
evands@3121
|
111 |
}
|
|
evands@3121
|
112 |
|
|
evands@3121
|
113 |
void PowerNotifier_init(void)
|
|
evands@3121
|
114 |
{
|
|
evands@3121
|
115 |
powerNotifierRunLoopSource = IOPSNotificationCreateRunLoopSource(powerSourceChanged,
|
|
evands@3121
|
116 |
NULL);
|
|
ingmarstein@3125
|
117 |
if (powerNotifierRunLoopSource)
|
|
evands@3121
|
118 |
CFRunLoopAddSource(CFRunLoopGetCurrent(), powerNotifierRunLoopSource, kCFRunLoopDefaultMode);
|
|
ingmarstein@3125
|
119 |
|
|
evands@3121
|
120 |
lastPowerSource = HGUnknownPower;
|
|
evands@3121
|
121 |
}
|
|
evands@3121
|
122 |
|
|
evands@3121
|
123 |
void PowerNotifier_dealloc(void)
|
|
evands@3121
|
124 |
{
|
|
ingmarstein@3125
|
125 |
CFRunLoopRemoveSource(CFRunLoopGetCurrent(), powerNotifierRunLoopSource, kCFRunLoopDefaultMode);
|
|
evands@3121
|
126 |
CFRelease(powerNotifierRunLoopSource);
|
|
evands@3121
|
127 |
}
|