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.
     1 /*
     2  *  PowerNotifier.c
     3  *  HardwareGrowler
     4  *
     5  *  Created by Evan Schoenberg on 3/27/06.
     6  *
     7  */
     8 
     9 #include "PowerNotifier.h"
    10 #include <IOKit/IOKitLib.h>
    11 #include <IOKit/ps/IOPSKeys.h>
    12 #include <IOKit/ps/IOPowerSources.h>
    13 
    14 #include "AppController.h"
    15 
    16 extern void NSLog(CFStringRef format, ...);
    17 
    18 static CFRunLoopSourceRef powerNotifierRunLoopSource = NULL;
    19 static HGPowerSource lastPowerSource;
    20 static CFBooleanRef lastChargingState;
    21 static int lastBatteryTime = -1;
    22 
    23 static bool stringsAreEqual(CFStringRef a, CFStringRef b)
    24 {
    25 	if (!a || !b) return 0;
    26 
    27 	return (CFStringCompare(a, b, 0) == kCFCompareEqualTo);
    28 }
    29 
    30 static void powerSourceChanged(void *context)
    31 {
    32 #pragma unused(context)
    33 	CFTypeRef	powerBlob = IOPSCopyPowerSourcesInfo();
    34 	CFArrayRef	powerSourcesList = IOPSCopyPowerSourcesList(powerBlob);
    35 
    36 	unsigned	count = CFArrayGetCount(powerSourcesList);
    37 	for (unsigned i = 0U; i < count; ++i) {
    38 		CFTypeRef		powerSource;
    39 		CFDictionaryRef description;
    40 
    41 		HGPowerSource	hgPowerSource;
    42 		CFBooleanRef	charging = kCFBooleanFalse;
    43 		int				batteryTime = -1;
    44 		int				percentageCapacity = -1;
    45 
    46 		powerSource = CFArrayGetValueAtIndex(powerSourcesList, i);
    47 		description = IOPSGetPowerSourceDescription(powerBlob, powerSource);
    48 
    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)
    51 			continue;
    52 
    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;
    61 			else
    62 				hgPowerSource = HGUnknownPower;
    63 
    64 			//Battery power
    65 			if (CFDictionaryGetValue(description, CFSTR(kIOPSIsChargingKey)) == kCFBooleanTrue) {
    66 				//Charging
    67 				charging = kCFBooleanTrue;
    68 
    69 				CFNumberRef timeToChargeNum = CFDictionaryGetValue(description, CFSTR(kIOPSTimeToFullChargeKey));
    70 				int timeToCharge;
    71 
    72 				if (CFNumberGetValue(timeToChargeNum, kCFNumberIntType, &timeToCharge))
    73 					batteryTime = timeToCharge;
    74 			} else {
    75 				//Not charging
    76 				charging = kCFBooleanFalse;
    77 
    78 				CFNumberRef timeToEmptyNum = CFDictionaryGetValue(description, CFSTR(kIOPSTimeToEmptyKey));
    79 				int timeToEmpty;
    80 
    81 				if (CFNumberGetValue(timeToEmptyNum, kCFNumberIntType, &timeToEmpty))
    82 					batteryTime = timeToEmpty;
    83 			}
    84 
    85 			/* Capacity */
    86 			CFNumberRef currentCapacityNum = CFDictionaryGetValue(description, CFSTR(kIOPSCurrentCapacityKey));
    87 			CFNumberRef maxCapacityNum = CFDictionaryGetValue(description, CFSTR(kIOPSMaxCapacityKey));
    88 
    89 			int currentCapacity, maxCapacity;
    90 
    91 			if (CFNumberGetValue(currentCapacityNum, kCFNumberIntType, &currentCapacity) &&
    92 					CFNumberGetValue(maxCapacityNum, kCFNumberIntType, &maxCapacity))
    93 				percentageCapacity = roundf((currentCapacity / (float)maxCapacity) * 100.0f);
    94 
    95 		} else {
    96 			//UPS power
    97 			hgPowerSource = HGUPSPower;
    98 		}
    99 
   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);
   106 		}
   107 	}
   108 
   109 	CFRelease(powerSourcesList);
   110 	CFRelease(powerBlob);
   111 }
   112 
   113 void PowerNotifier_init(void)
   114 {
   115 	powerNotifierRunLoopSource = IOPSNotificationCreateRunLoopSource(powerSourceChanged,
   116 																	 NULL);
   117 	if (powerNotifierRunLoopSource)
   118 		CFRunLoopAddSource(CFRunLoopGetCurrent(), powerNotifierRunLoopSource, kCFRunLoopDefaultMode);
   119 
   120 	lastPowerSource = HGUnknownPower;
   121 }
   122 
   123 void PowerNotifier_dealloc(void)
   124 {
   125 	CFRunLoopRemoveSource(CFRunLoopGetCurrent(), powerNotifierRunLoopSource, kCFRunLoopDefaultMode);
   126 	CFRelease(powerNotifierRunLoopSource);
   127 }