Extras/HardwareGrowler/PowerNotifier.c
author Peter Hosey
Tue May 05 00:50:25 2009 -0700 (2009-05-05)
changeset 4205 f3398597ecdd
parent 3308 19ee5964c2c4
child 4505 a1733bc0be40
permissions -rw-r--r--
Make the handling of charging changes more sane:
1. Allow multiple notifications for the same power source (namely, the AC adapter) if the charging state changes.
2. We now use coalesce notifications on the title of the notification, so the AC-adapter notification only appears once, with the charging or not-charging notification replacing the previous not-charging or charging notification. Consecutive notifications with the same charging state remain suppressed.
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, &currentCapacity) &&
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
}