diff --git a/AppController.h b/AppController.h new file mode 100644 index 0000000..6c35ade --- /dev/null +++ b/AppController.h @@ -0,0 +1,47 @@ +/* AppController */ + +#ifndef APPCONTROLLER_H +#define APPCONTROLLER_H + +#include + +#include "PowerNotifier.h" + +void AppController_fwDidConnect(CFStringRef deviceName); +void AppController_fwDidDisconnect(CFStringRef deviceName); +void AppController_usbDidConnect(CFStringRef deviceName); +void AppController_usbDidDisconnect(CFStringRef deviceName); +void AppController_bluetoothDidConnect(CFStringRef device); +void AppController_bluetoothDidDisconnect(CFStringRef device); +void AppController_airportConnect(CFStringRef networkName, const unsigned char *bssidBytes); +void AppController_airportDisconnect(CFStringRef networkName); +void AppController_linkUp(CFStringRef description); +void AppController_linkDown(CFStringRef description); +void AppController_ipAcquired(CFStringRef ip, CFStringRef type); +void AppController_ipReleased(void); +void AppController_syncStarted(void); +void AppController_syncFinished(void); +void AppController_powerSwitched(HGPowerSource powerSource, CFBooleanRef isCharging, + CFIndex batteryTime, CFIndex batteryPercentage); + +#ifdef __OBJC__ + +#import + +#import "VolumeNotifier.h" +#import "NetworkNotifier.h" + +void AppController_volumeDidMount(VolumeInfo *info); +void AppController_volumeDidUnmount(VolumeInfo *info); + +@interface AppController : NSObject { + NetworkNotifier *networkNotifier; +} + +- (IBAction) doSimpleHelp:(id)sender; + +@end + +#endif + +#endif diff --git a/AppController.m b/AppController.m new file mode 100644 index 0000000..6d672b1 --- /dev/null +++ b/AppController.m @@ -0,0 +1,416 @@ +#import "AppController.h" +#include +#include +#include +#include "FireWireNotifier.h" +#include "USBNotifier.h" +#include "BluetoothNotifier.h" +#include "VolumeNotifier.h" +#include "NetworkNotifier.h" +#include "SyncNotifier.h" +#include "PowerNotifier.h" + +#define NotifierUSBConnectionNotification @"USB Device Connected" +#define NotifierUSBDisconnectionNotification @"USB Device Disconnected" +#define NotifierVolumeMountedNotification @"Volume Mounted" +#define NotifierVolumeUnmountedNotification @"Volume Unmounted" +#define NotifierBluetoothConnectionNotification @"Bluetooth Device Connected" +#define NotifierBluetoothDisconnectionNotification @"Bluetooth Device Disconnected" +#define NotifierFireWireConnectionNotification @"FireWire Device Connected" +#define NotifierFireWireDisconnectionNotification @"FireWire Device Disconnected" +#define NotifierNetworkLinkUpNotification @"Network Link Up" +#define NotifierNetworkLinkDownNotification @"Network Link Down" +#define NotifierNetworkIpAcquiredNotification @"IP Acquired" +#define NotifierNetworkIpReleasedNotification @"IP Released" +#define NotifierNetworkAirportConnectNotification @"AirPort Connected" +#define NotifierNetworkAirportDisconnectNotification @"AirPort Disconnected" +#define NotifierSyncStartedNotification @"Sync started" +#define NotifierSyncFinishedNotification @"Sync finished" +#define NotifierPowerOnACNotification @"Switched to A/C Power" +#define NotifierPowerOnBatteryNotification @"Switched to Battery Power" +#define NotifierPowerOnUPSNotification @"Switched to UPS Power" + +#define NotifierUSBConnectionHumanReadableDescription NSLocalizedString(@"USB Device Connected", "") +#define NotifierUSBDisconnectionHumanReadableDescription NSLocalizedString(@"USB Device Disconnected", "") +#define NotifierVolumeMountedHumanReadableDescription NSLocalizedString(@"Volume Mounted", "") +#define NotifierVolumeUnmountedHumanReadableDescription NSLocalizedString(@"Volume Unmounted", "") +#define NotifierBluetoothConnectionHumanReadableDescription NSLocalizedString(@"Bluetooth Device Connected", "") +#define NotifierBluetoothDisconnectionHumanReadableDescription NSLocalizedString(@"Bluetooth Device Disconnected", "") +#define NotifierFireWireConnectionHumanReadableDescription NSLocalizedString(@"FireWire Device Connected", "") +#define NotifierFireWireDisconnectionHumanReadableDescription NSLocalizedString(@"FireWire Device Disconnected", "") +#define NotifierNetworkLinkUpHumanReadableDescription NSLocalizedString(@"Network Link Up", "") +#define NotifierNetworkLinkDownHumanReadableDescription NSLocalizedString(@"Network Link Down", "") +#define NotifierNetworkIpAcquiredHumanReadableDescription NSLocalizedString(@"IP Acquired", "") +#define NotifierNetworkIpReleasedHumanReadableDescription NSLocalizedString(@"IP Released", "") +#define NotifierNetworkAirportConnectHumanReadableDescription NSLocalizedString(@"AirPort Connected", "") +#define NotifierNetworkAirportDisconnectHumanReadableDescription NSLocalizedString(@"AirPort Disconnected", "") +#define NotifierSyncStartedHumanReadableDescription NSLocalizedString(@"Sync started", "") +#define NotifierSyncFinishedHumanReadableDescription NSLocalizedString(@"Sync finished", "") +#define NotifierPowerOnACHumanReadableDescription NSLocalizedString(@"Switched to A/C Power", "") +#define NotifierPowerOnBatteryHumanReadableDescription NSLocalizedString(@"Switched to Battery Power", "") +#define NotifierPowerOnUPSHumanReadableDescription NSLocalizedString(@"Switched to UPS Power", "") + + +#define NotifierFireWireConnectionTitle() CFCopyLocalizedString(CFSTR("FireWire Connection"), "") +#define NotifierFireWireDisconnectionTitle() CFCopyLocalizedString(CFSTR("FireWire Disconnection"), "") +#define NotifierUSBConnectionTitle() CFCopyLocalizedString(CFSTR("USB Connection"), "") +#define NotifierUSBDisconnectionTitle() CFCopyLocalizedString(CFSTR("USB Disconnection"), "") +#define NotifierBluetoothConnectionTitle() CFCopyLocalizedString(CFSTR("Bluetooth Connection"), "") +#define NotifierBluetoothDisconnectionTitle() CFCopyLocalizedString(CFSTR("Bluetooth Disconnection"), "") +#define NotifierVolumeMountedTitle() CFCopyLocalizedString(CFSTR("Volume Mounted"), "") +#define NotifierVolumeUnmountedTitle() CFCopyLocalizedString(CFSTR("Volume Unmounted"), "") +#define NotifierNetworkAirportConnectTitle() CFCopyLocalizedString(CFSTR("Airport connected"), "") +#define NotifierNetworkAirportDisconnectTitle() CFCopyLocalizedString(CFSTR("Airport disconnected"), "") +#define NotifierNetworkLinkUpTitle() CFCopyLocalizedString(CFSTR("Ethernet activated"), "") +#define NotifierNetworkLinkDownTitle() CFCopyLocalizedString(CFSTR("Ethernet deactivated"), "") +#define NotifierNetworkIpAcquiredTitle() CFCopyLocalizedString(CFSTR("IP address acquired"), "") +#define NotifierNetworkIpReleasedTitle() CFCopyLocalizedString(CFSTR("IP address released"), "") +#define NotifierSyncStartedTitle() CFCopyLocalizedString(CFSTR("Sync started"), "") +#define NotifierSyncFinishedTitle() CFCopyLocalizedString(CFSTR("Sync finished"), "") + +#define NotifierNetworkAirportDisconnectDescription() CFCopyLocalizedString(CFSTR("Left network %@."), "") +#define NotifierNetworkIpAcquiredDescription() CFCopyLocalizedString(CFSTR("New primary IP: %@ (%@)"), "") +#define NotifierNetworkIpReleasedDescription() CFCopyLocalizedString(CFSTR("No IP address now"), "") + +static io_connect_t powerConnection; +static io_object_t powerNotifier; +static CFRunLoopSourceRef powerRunLoopSource; +static BOOL sleeping; + +#ifdef __OBJC__ +# define DATA_TYPE NSData * +# define DATE_TYPE NSDate * +# define DICTIONARY_TYPE NSDictionary * +# define MUTABLE_DICTIONARY_TYPE NSMutableDictionary * +# define STRING_TYPE NSString * +# define ARRAY_TYPE NSArray * +# define URL_TYPE NSURL * +# define PLIST_TYPE NSObject * +# define OBJECT_TYPE id +# define BOOL_TYPE BOOL +#else +# include +# define DATA_TYPE CFDataRef +# define DATE_TYPE CFDateRef +# define DICTIONARY_TYPE CFDictionaryRef +# define MUTABLE_DICTIONARY_TYPE CFMutableDictionaryRef +# define STRING_TYPE CFStringRef +# define ARRAY_TYPE CFArrayRef +# define URL_TYPE CFURLRef +# define PLIST_TYPE CFPropertyListRef +# define OBJECT_TYPE CFTypeRef +# define BOOL_TYPE Boolean +#endif + +NSUserNotificationCenter *center; + + +static void sendNotification(NSString *title, NSString *text) { + + NSUserNotification *notification = [[NSUserNotification alloc] init]; + [notification setTitle:title]; + [notification setInformativeText:text]; + [notification setDeliveryDate:[NSDate dateWithTimeInterval:1 sinceDate:[NSDate date]]]; + [notification setSoundName:NSUserNotificationDefaultSoundName]; + [notification setHasActionButton:false]; + [center scheduleNotification:notification]; + [notification release]; +} + +#pragma mark Firewire + +void AppController_fwDidConnect(CFStringRef deviceName) { + NSLog(@"FireWire Connect: %@", deviceName); + CFStringRef title = NotifierFireWireConnectionTitle(); + sendNotification((NSString *)title, (NSString *)deviceName); + CFRelease(title); +} + +void AppController_fwDidDisconnect(CFStringRef deviceName) { + NSLog(@"FireWire Disconnect: %@", deviceName); + CFStringRef title = NotifierFireWireDisconnectionTitle(); + sendNotification((NSString *)title, (NSString *)deviceName); + CFRelease(title); +} + +#pragma mark USB + +void AppController_usbDidConnect(CFStringRef deviceName) { + NSLog(@"USB Connect: %@", deviceName); + CFStringRef title = NotifierUSBConnectionTitle(); + sendNotification((NSString *)title, (NSString *)deviceName); + CFRelease(title); +} + +void AppController_usbDidDisconnect(CFStringRef deviceName) { + NSLog(@"USB Disconnect: %@", deviceName); + CFStringRef title = NotifierUSBDisconnectionTitle(); + sendNotification((NSString *)title, (NSString *)deviceName); + CFRelease(title); +} + +#pragma mark Bluetooth + +void AppController_bluetoothDidConnect(CFStringRef device) { + NSLog(@"Bluetooth Connect: %@", device); + CFStringRef title = NotifierBluetoothConnectionTitle(); + sendNotification((NSString *)title, (NSString *)device); + CFRelease(title); +} + +void AppController_bluetoothDidDisconnect(CFStringRef device) { + NSLog(@"Bluetooth Disconnect: %@", device); + CFStringRef title = NotifierBluetoothDisconnectionTitle(); + sendNotification((NSString *)title, (NSString *)device); + CFRelease(title); +} + +#pragma mark Volumes + +void AppController_volumeDidMount(VolumeInfo *info) { + NSLog(@"volume Mount: %@", info); + CFStringRef title = NotifierVolumeMountedTitle(); + NSDictionary *context = nil; + + if ([info path]) { + context = [NSDictionary dictionaryWithObjectsAndKeys: + (NSString *)NotifierVolumeMountedNotification, @"notification", + [info path], @"path", + nil]; + } + sendNotification((NSString *)title, (NSString *)[info name]); + CFRelease(title); +} + +void AppController_volumeDidUnmount(VolumeInfo *info) { + NSLog(@"volume Unmount: %@", info); + CFStringRef title = NotifierVolumeUnmountedTitle(); + sendNotification((NSString *)title, (NSString *)[info name]); + CFRelease(title); +} + +#pragma mark Network + +void AppController_airportConnect(CFStringRef networkName, const unsigned char *bssidBytes) { + + + if (sleeping) + return; + + CFStringRef title = NotifierNetworkAirportConnectTitle(); + + NSString *bssid = [NSString stringWithFormat:@"%02X:%02X:%02X:%02X:%02X:%02X", + bssidBytes[0], + bssidBytes[1], + bssidBytes[2], + bssidBytes[3], + bssidBytes[4], + bssidBytes[5]]; + NSString *description = [NSString stringWithFormat:NSLocalizedString(@"Joined network.\nSSID:\t\t%@\nBSSID:\t%@", ""), + networkName, + bssid]; + NSLog(@"AirPort connect: %@", description); + sendNotification((NSString *)title, (NSString *)description); + CFRelease(title); +} + +void AppController_airportDisconnect(CFStringRef networkName) { + if (sleeping) + return; + CFStringRef title = NotifierNetworkAirportDisconnectTitle(); + CFStringRef format = NotifierNetworkAirportDisconnectDescription(); + CFStringRef description = CFStringCreateWithFormat(kCFAllocatorDefault, + NULL, + format, + networkName); + NSLog(@"AirPort disconnect: %@", description); + CFRelease(format); + sendNotification((NSString *)title, (NSString *)description); + CFRelease(title); + CFRelease(description); +} + +void AppController_linkUp(CFStringRef description) { + NSLog(@"Link up: %@", description); + if (sleeping) + return; + CFStringRef title = NotifierNetworkLinkUpTitle(); + sendNotification((NSString *)title, (NSString *)description); + CFRelease(title); +} + +void AppController_linkDown(CFStringRef description) { + NSLog(@"Link down: %@", description); + if (sleeping) + return; + CFStringRef title = NotifierNetworkLinkDownTitle(); + sendNotification((NSString *)title, (NSString *)description); + CFRelease(title); +} + +void AppController_ipAcquired(CFStringRef ip, CFStringRef type) { + NSLog(@"IP acquired: %@", ip); + if (sleeping) + return; + CFStringRef title = NotifierNetworkIpAcquiredTitle(); + CFStringRef format = NotifierNetworkIpAcquiredDescription(); + CFStringRef description = CFStringCreateWithFormat(kCFAllocatorDefault, + NULL, + format, + ip, + type); + CFRelease(format); + sendNotification((NSString *)title, (NSString *)description); + CFRelease(title); + CFRelease(description); +} + +void AppController_ipReleased(void) { + NSLog(@"IP released"); + if (sleeping) + return; + CFStringRef title = NotifierNetworkIpReleasedTitle(); + CFStringRef description = NotifierNetworkIpReleasedDescription(); + sendNotification((NSString *)title, (NSString *)description); + CFRelease(title); + CFRelease(description); +} + +#pragma mark Sync + +void AppController_syncStarted(void) { + NSLog(@"Sync started"); + CFStringRef title = NotifierSyncStartedTitle(); + sendNotification((NSString *)title, (NSString *)title); + CFRelease(title); +} + +void AppController_syncFinished(void) { + NSLog(@"Sync finished"); + CFStringRef title = NotifierSyncFinishedTitle(); + sendNotification((NSString *)title, (NSString *)title); + CFRelease(title); +} + +#pragma mark Power +void AppController_powerSwitched(HGPowerSource powerSource, CFBooleanRef isCharging, + CFIndex batteryTime, CFIndex batteryPercentage) +{ + NSString *title = nil; + NSMutableString *description = [NSMutableString string]; + NSString *notificationName = nil; + BOOL haveBatteryTime = (batteryTime != -1); + BOOL haveBatteryPercentage = (batteryPercentage != -1); + if (powerSource == HGACPower) { + title = NSLocalizedString(@"On A/C power", nil); + if (isCharging == kCFBooleanTrue) { + [description appendString:NSLocalizedString(@"Battery charging...", nil)]; + if (haveBatteryTime || haveBatteryPercentage) [description appendString:@"\n"]; + if (haveBatteryTime) [description appendFormat:NSLocalizedString(@"Time to charge: %i minutes", nil), batteryTime]; + if (haveBatteryTime && haveBatteryPercentage) [description appendString:@"\n"]; + if (haveBatteryPercentage) [description appendFormat:NSLocalizedString(@"Current charge: %d%%", nil), batteryPercentage]; + } else { + + } + + notificationName = (NSString *)NotifierPowerOnACNotification; + + } else if (powerSource == HGBatteryPower) { + title = NSLocalizedString(@"On battery power", nil); + if (haveBatteryTime) [description appendFormat:NSLocalizedString(@"Time remaining: %i minutes", nil), batteryTime]; + if (haveBatteryTime && haveBatteryPercentage) [description appendString:@"\n"]; + if (haveBatteryPercentage) [description appendFormat:NSLocalizedString(@"Current charge: %d%%", nil), batteryPercentage]; + notificationName = (NSString *)NotifierPowerOnBatteryNotification; + } else if (powerSource == HGUPSPower) { + title = NSLocalizedString(@"On UPS power", nil); + notificationName = (NSString *)NotifierPowerOnUPSNotification; + } + if (notificationName) { + sendNotification((NSString *)title, (NSString *)description); + } +} + +static void powerCallback(void *refcon, io_service_t service, natural_t messageType, void *messageArgument) { +#pragma unused(refcon,service) + switch (messageType) { + case kIOMessageSystemWillRestart: + case kIOMessageSystemWillPowerOff: + case kIOMessageSystemWillSleep: + case kIOMessageDeviceWillPowerOff: + sleeping = YES; + IOAllowPowerChange(powerConnection, (long)messageArgument); + break; + case kIOMessageCanSystemPowerOff: + case kIOMessageCanSystemSleep: + case kIOMessageCanDevicePowerOff: + IOAllowPowerChange(powerConnection, (long)messageArgument); + break; + case kIOMessageSystemWillNotSleep: + case kIOMessageSystemWillNotPowerOff: + case kIOMessageSystemHasPoweredOn: + case kIOMessageDeviceWillNotPowerOff: + case kIOMessageDeviceHasPoweredOn: + sleeping = NO; + default: + break; + } +} + +@implementation AppController + +- (void) userNotificationCenter: (NSUserNotificationCenter *) center didActivateNotification: (NSUserNotification *) notification +{ + NSLog(@"Notficiation clicked"); + CFStringRef title = (CFStringRef)notification.title; + CFStringRef informativeText = (CFStringRef)notification.informativeText; + [center removeDeliveredNotification:notification]; + CFOptionFlags options = kCFUserNotificationNoteAlertLevel; + CFOptionFlags responseFlags = 0; + CFUserNotificationDisplayAlert(0, options, NULL, NULL, NULL, + title, + informativeText, NULL, + NULL,NULL, &responseFlags); +} + +- (void) awakeFromNib { + NSLog(@"Starting..."); + center = [NSUserNotificationCenter defaultUserNotificationCenter]; + [center setDelegate:self]; + sendNotification(@"HardwareGrowler", @"Starting..."); + // Register for sleep and wake notifications so we can suppress various notifications during sleep + IONotificationPortRef ioNotificationPort; + powerConnection = IORegisterForSystemPower(NULL, &ioNotificationPort, powerCallback, &powerNotifier); + if (powerConnection) { + powerRunLoopSource = IONotificationPortGetRunLoopSource(ioNotificationPort); + CFRunLoopAddSource(CFRunLoopGetCurrent(), powerRunLoopSource, kCFRunLoopDefaultMode); + } + FireWireNotifier_init(); + USBNotifier_init(); + VolumeNotifier_init(); + SyncNotifier_init(); + BluetoothNotifier_init(); + networkNotifier = [[NetworkNotifier alloc] init]; + PowerNotifier_init(); + +} + +- (void) dealloc { + FireWireNotifier_dealloc(); + USBNotifier_dealloc(); + VolumeNotifier_dealloc(); + SyncNotifier_dealloc(); + BluetoothNotifier_dealloc(); + [networkNotifier release]; + if (powerConnection) { + CFRunLoopRemoveSource(CFRunLoopGetCurrent(), powerRunLoopSource, kCFRunLoopDefaultMode); + IODeregisterForSystemPower(&powerNotifier); + } + [super dealloc]; +} + +- (IBAction) doSimpleHelp:(id)sender { +#pragma unused(sender) + [[NSWorkspace sharedWorkspace] openFile:[[NSBundle mainBundle] pathForResource:@"readme" ofType:@"txt"]]; +} + +@end diff --git a/BluetoothNotifier.c b/BluetoothNotifier.c new file mode 100644 index 0000000..232bea1 --- /dev/null +++ b/BluetoothNotifier.c @@ -0,0 +1,62 @@ +#include "BluetoothNotifier.h" +#include "AppController.h" +#include +#include +#include + +extern void NSLog(CFStringRef format, ...); + +static IOBluetoothUserNotificationRef connectionNotification; +static Boolean initializing; + +static void bluetoothDisconnection(void *userRefCon, IOBluetoothUserNotificationRef inRef, IOBluetoothObjectRef objectRef) { +#pragma unused(userRefCon) + // NSLog(@"BT Device Disconnection: %@" , [device name]); + AppController_bluetoothDidDisconnect(IOBluetoothDeviceGetName(objectRef)); + + IOBluetoothUserNotificationUnregister(inRef); +} + +static void bluetoothConnection(void *userRefCon, IOBluetoothUserNotificationRef inRef, IOBluetoothObjectRef objectRef) { +#pragma unused(userRefCon,status,inRef) + // NSLog(@"BT Device connection: %@" , [device name]); + Boolean keyExistsAndHasValidFormat; + if (!initializing || CFPreferencesGetAppBooleanValue(CFSTR("ShowExisting"), CFSTR("com.growl.hardwaregrowler"), &keyExistsAndHasValidFormat)) + AppController_bluetoothDidConnect(IOBluetoothDeviceGetName(objectRef)); + + IOBluetoothDeviceRegisterForDisconnectNotification(objectRef, bluetoothDisconnection, NULL); +} + +/* +static void channelOpened(IOBluetoothUserNotification*)note withChannel: (IOBluetoothRFCOMMChannel *) chan { + NSLog(@"BT Channel opened."); + + NSLog(@"%@" , [[chan getDevice] name]); + + [chan registerForChannelCloseNotification: self + selector: @selector(channelClosed:withChannel:)]; + +} + +static void channelClosed(IOBluetoothUserNotification*)note withChannel: (IOBluetoothRFCOMMChannel *) chan { + NSLog(@"BT Channel closed. %@" , note); +} +*/ + +void BluetoothNotifier_init(void) { + initializing = true; +// NSLog(@"registering for BT Notes."); + /* + [IOBluetoothRFCOMMChannel registerForChannelOpenNotifications: self + selector: @selector(channelOpened:withChannel:) + withChannelID: 0 + direction: kIOBluetoothUserNotificationChannelDirectionAny]; + */ + + connectionNotification = IOBluetoothRegisterForDeviceConnectNotifications(bluetoothConnection, NULL); + initializing = false; +} + +void BluetoothNotifier_dealloc(void) { + IOBluetoothUserNotificationUnregister(connectionNotification); +} diff --git a/BluetoothNotifier.h b/BluetoothNotifier.h new file mode 100644 index 0000000..81d9d22 --- /dev/null +++ b/BluetoothNotifier.h @@ -0,0 +1,9 @@ +/* BluetoothNotifier */ + +#ifndef BLUETOOTH_NOTIFIER_H +#define BLUETOOTH_NOTIFIER_H + +void BluetoothNotifier_init(void); +void BluetoothNotifier_dealloc(void); + +#endif diff --git a/English.lproj/Credits.rtf b/English.lproj/Credits.rtf new file mode 100644 index 0000000..ebf60ad --- /dev/null +++ b/English.lproj/Credits.rtf @@ -0,0 +1,9 @@ +{\rtf1\mac\ansicpg10000\cocoartf102 +{\fonttbl\f0\fnil\fcharset77 HoeflerText-Regular;\f1\fswiss\fcharset77 Helvetica;} +{\colortbl;\red255\green255\blue255;} +\pard\tx565\tx1133\tx1700\tx2266\tx2832\tx3401\tx3967\tx4535\tx5102\tx5669\tx6235\tx6802\qc + +\f0\fs36 \cf0 Grrrr... +\f1\fs24 \ +\pard\tx565\tx1133\tx1700\tx2266\tx2832\tx3401\tx3967\tx4535\tx5102\tx5669\tx6235\tx6802\sl480\slmult1\qc +\cf0 A Growl Extra for hardware notifications.} \ No newline at end of file diff --git a/English.lproj/InfoPlist.strings b/English.lproj/InfoPlist.strings new file mode 100644 index 0000000..9c30d38 --- /dev/null +++ b/English.lproj/InfoPlist.strings @@ -0,0 +1,4 @@ +/* Localized versions of Info.plist keys */ + +CFBundleName = "HardwareGrowler"; +NSHumanReadableCopyright = "Copyright (c) 2004-2010, Growl Team."; diff --git a/English.lproj/Localizable.strings b/English.lproj/Localizable.strings new file mode 100644 index 0000000..a2f263e --- /dev/null +++ b/English.lproj/Localizable.strings @@ -0,0 +1,165 @@ +/* Type of IP address */ +"6to4 relay" = "6to4 relay"; + +/* No comment provided by engineer. */ +"AirPort Connected" = "AirPort Connected"; + +/* No comment provided by engineer. */ +"Airport connected" = "Airport connected"; + +/* No comment provided by engineer. */ +"Airport disconnected" = "Airport disconnected"; + +/* No comment provided by engineer. */ +"AirPort Disconnected" = "AirPort Disconnected"; + +/* No comment provided by engineer. */ +"Battery charging..." = "Battery charging..."; + +/* Type of IP address */ +"Benchmark" = "Benchmark"; + +/* No comment provided by engineer. */ +"Bluetooth Connection" = "Bluetooth Connection"; + +/* No comment provided by engineer. */ +"Bluetooth Device Connected" = "Bluetooth Device Connected"; + +/* No comment provided by engineer. */ +"Bluetooth Device Disconnected" = "Bluetooth Device Disconnected"; + +/* No comment provided by engineer. */ +"Bluetooth Disconnection" = "Bluetooth Disconnection"; + +/* No comment provided by engineer. */ +"Current charge: %d%%" = "Current charge: %d%%"; + +/* No comment provided by engineer. */ +"Ethernet activated" = "Ethernet activated"; + +/* No comment provided by engineer. */ +"Ethernet deactivated" = "Ethernet deactivated"; + +/* No comment provided by engineer. */ +"FireWire Connection" = "FireWire Connection"; + +/* No comment provided by engineer. */ +"FireWire Device Connected" = "FireWire Device Connected"; + +/* No comment provided by engineer. */ +"FireWire Device Disconnected" = "FireWire Device Disconnected"; + +/* No comment provided by engineer. */ +"FireWire Disconnection" = "FireWire Disconnection"; + +/* No comment provided by engineer. */ +"Interface:\ten0" = "Interface:\ten0"; + +/* The %@ will be replaced by a description of the Ethernet media such as '100BT/full-duplex' */ +"Interface:\ten0\nMedia:\t%@" = "Interface:\ten0\nMedia:\t%@"; + +/* No comment provided by engineer. */ +"IP Acquired" = "IP Acquired"; + +/* No comment provided by engineer. */ +"IP address acquired" = "IP address acquired"; + +/* No comment provided by engineer. */ +"IP address released" = "IP address released"; + +/* No comment provided by engineer. */ +"IP Released" = "IP Released"; + +/* No comment provided by engineer. */ +"Joined network.\nSSID:\t\t%@\nBSSID:\t%@" = "Joined network.\nSSID:\t\t%1$@\nBSSID:\t%2$@"; + +/* No comment provided by engineer. */ +"Left network %@." = "Left network %@."; + +/* Type of IP address */ +"Link-local" = "Link-local"; + +/* Type of IP address */ +"Loopback" = "Loopback"; + +/* No comment provided by engineer. */ +"Network Link Down" = "Network Link Down"; + +/* No comment provided by engineer. */ +"Network Link Up" = "Network Link Up"; + +/* No comment provided by engineer. */ +"New primary IP: %@ (%@)" = "New primary IP: %1$@ (%2$@)"; + +/* No comment provided by engineer. */ +"No IP address now" = "No IP address now"; + +/* No comment provided by engineer. */ +"On A/C power" = "On A/C power"; + +/* No comment provided by engineer. */ +"On battery power" = "On battery power"; + +/* No comment provided by engineer. */ +"On UPS power" = "On UPS power"; + +/* Type of IP address */ +"Private" = "Private"; + +/* Type of IP address */ +"Public" = "Public"; + +/* Type of IP address */ +"Reserved" = "Reserved"; + +/* No comment provided by engineer. */ +"Switched to A/C Power" = "Switched to A/C Power"; + +/* No comment provided by engineer. */ +"Switched to Battery Power" = "Switched to Battery Power"; + +/* No comment provided by engineer. */ +"Switched to UPS Power" = "Switched to UPS Power"; + +/* No comment provided by engineer. */ +"Sync finished" = "Sync finished"; + +/* No comment provided by engineer. */ +"Sync started" = "Sync started"; + +/* Type of IP address */ +"Test" = "Test"; + +/* No comment provided by engineer. */ +"Time remaining: %i minutes" = "Time remaining: %i minutes"; + +/* No comment provided by engineer. */ +"Time to charge: %i minutes" = "Time to charge: %i minutes"; + +/* No comment provided by engineer. */ +"Unnamed FireWire Device" = "Unnamed FireWire Device"; + +/* No comment provided by engineer. */ +"USB 2.0 Bus" = "USB 2.0 Bus"; + +/* No comment provided by engineer. */ +"USB Bus" = "USB Bus"; + +/* No comment provided by engineer. */ +"USB Connection" = "USB Connection"; + +/* No comment provided by engineer. */ +"USB Device Connected" = "USB Device Connected"; + +/* No comment provided by engineer. */ +"USB Device Disconnected" = "USB Device Disconnected"; + +/* No comment provided by engineer. */ +"USB Disconnection" = "USB Disconnection"; + +/* No comment provided by engineer. */ +"Volume Mounted" = "Volume Mounted"; + +/* No comment provided by engineer. */ +"Volume Unmounted" = "Volume Unmounted"; + diff --git a/English.lproj/MainMenu.nib/designable.nib b/English.lproj/MainMenu.nib/designable.nib new file mode 100644 index 0000000..f458927 --- /dev/null +++ b/English.lproj/MainMenu.nib/designable.nib @@ -0,0 +1,589 @@ + + + + 1080 + 12A269 + 2549 + 1187 + 624.00 + + com.apple.InterfaceBuilder.CocoaPlugin + 2549 + + + NSButton + NSButtonCell + NSCustomObject + NSMenu + NSMenuItem + NSUserDefaultsController + NSView + NSWindowTemplate + + + com.apple.InterfaceBuilder.CocoaPlugin + + + PluginDependencyRecalculationVersion + + + + + + NSApplication + + + + FirstResponder + + + NSApplication + + + MainMenu + + + + HardwareGrowler + + 1048576 + 2147483647 + + NSImage + NSMenuCheckmark + + + NSImage + NSMenuMixedState + + submenuAction: + + HardwareGrowler + + + + About HardwareGrowler + + 2147483647 + + + + + + YES + YES + + + 1048576 + 2147483647 + + + + + + Preferences… + , + 1048576 + 2147483647 + + + + + + YES + YES + + + 1048576 + 2147483647 + + + + + + Services + + 1048576 + 2147483647 + + + submenuAction: + + + Services + + + _NSServicesMenu + + + + + YES + YES + + + 1048576 + 2147483647 + + + + + + Hide HardwareGrowler + h + 1048576 + 2147483647 + + + + + + Hide Others + h + 1572864 + 2147483647 + + + + + + Show All + + 1048576 + 2147483647 + + + + + + YES + YES + + + 1048576 + 2147483647 + + + + + + Quit HardwareGrowler + q + 1048576 + 2147483647 + + + + + _NSAppleMenu + + + + + Help + + 1048576 + 2147483647 + + + submenuAction: + + Help + + + + HardwareGrowler Help + ? + 1048576 + 2147483647 + + + + + + + + _NSMainMenu + + + AppController + + + 3 + 2 + {{59, 662}, {269, 107}} + 1886912512 + Preferences + + NSPanel + + + View + + + {213, 107} + + + 256 + + + + 256 + {{18, 40}, {213, 37}} + + YES + + 67108864 + 0 + IFNob3cgY29ubmVjdGVkIGRldmljZXMKIHdoZW4gSGFyZHdhcmVHcm93bGVyIHN0YXJ0cw + + LucidaGrande + 13 + 1044 + + + 1211912448 + 2 + + NSImage + NSSwitch + + + NSSwitch + + + + 200 + 25 + + NO + + + {269, 107} + + {{0, 0}, {1440, 878}} + {213, 129} + {10000000000000, 10000000000000} + preferencesWindow + YES + + + YES + + + + + + + terminate: + + + + 139 + + + + orderFrontStandardAboutPanel: + + + + 142 + + + + hideOtherApplications: + + + + 146 + + + + hide: + + + + 152 + + + + unhideAllApplications: + + + + 153 + + + + doSimpleHelp: + + + + 208 + + + + makeKeyAndOrderFront: + + + + 211 + + + + value: values.ShowExisting + + + + + + value: values.ShowExisting + value + values.ShowExisting + 2 + + + 215 + + + + + + 0 + + + + + + -2 + + + File's Owner + + + -1 + + + First Responder + + + 29 + + + + + + + MainMenu + + + 56 + + + + + + + + 57 + + + + + + + + + + + + + + + + + + 58 + + + + + 129 + + + + + 131 + + + + + + + + 130 + + + + + 134 + + + + + 136 + + + + + 143 + + + + + 144 + + + + + 145 + + + + + 149 + + + + + 150 + + + + + 196 + + + + + 103 + + + + + + + + 106 + + + + + + + + 111 + + + + + 204 + + + AppController + + + 209 + + + + + + PreferencesPanel + + + 210 + + + + + + + + 212 + + + + + + + + 213 + + + Shared Defaults + + + 217 + + + + + -3 + + + Application + + + + + com.apple.InterfaceBuilder.CocoaPlugin + com.apple.InterfaceBuilder.CocoaPlugin + com.apple.InterfaceBuilder.CocoaPlugin + com.apple.InterfaceBuilder.CocoaPlugin + com.apple.InterfaceBuilder.CocoaPlugin + com.apple.InterfaceBuilder.CocoaPlugin + com.apple.InterfaceBuilder.CocoaPlugin + com.apple.InterfaceBuilder.CocoaPlugin + com.apple.InterfaceBuilder.CocoaPlugin + com.apple.InterfaceBuilder.CocoaPlugin + com.apple.InterfaceBuilder.CocoaPlugin + com.apple.InterfaceBuilder.CocoaPlugin + com.apple.InterfaceBuilder.CocoaPlugin + com.apple.InterfaceBuilder.CocoaPlugin + com.apple.InterfaceBuilder.CocoaPlugin + com.apple.InterfaceBuilder.CocoaPlugin + com.apple.InterfaceBuilder.CocoaPlugin + com.apple.InterfaceBuilder.CocoaPlugin + com.apple.InterfaceBuilder.CocoaPlugin + com.apple.InterfaceBuilder.CocoaPlugin + com.apple.InterfaceBuilder.CocoaPlugin + com.apple.InterfaceBuilder.CocoaPlugin + com.apple.InterfaceBuilder.CocoaPlugin + com.apple.InterfaceBuilder.CocoaPlugin + com.apple.InterfaceBuilder.CocoaPlugin + com.apple.InterfaceBuilder.CocoaPlugin + com.apple.InterfaceBuilder.CocoaPlugin + + + + + + 217 + + + 0 + IBCocoaFramework + + com.apple.InterfaceBuilder.CocoaPlugin.InterfaceBuilder3 + + + YES + 3 + + {11, 11} + {10, 3} + {15, 15} + + + diff --git a/English.lproj/MainMenu.nib/keyedobjects.nib b/English.lproj/MainMenu.nib/keyedobjects.nib new file mode 100644 index 0000000..28faf47 Binary files /dev/null and b/English.lproj/MainMenu.nib/keyedobjects.nib differ diff --git a/FireWireNotifier.c b/FireWireNotifier.c new file mode 100644 index 0000000..94d3399 --- /dev/null +++ b/FireWireNotifier.c @@ -0,0 +1,156 @@ +#include "FireWireNotifier.h" +#include "AppController.h" +#include + +extern void NSLog(CFStringRef format, ...); + +static IONotificationPortRef ioKitNotificationPort; +static CFRunLoopSourceRef notificationRunLoopSource; +static Boolean notificationsArePrimed = false; + +static CFStringRef nameForFireWireObject(io_object_t thisObject) { + // This works with USB devices... + // but apparently not firewire + kern_return_t nameResult; + io_name_t deviceNameChars; + + nameResult = IORegistryEntryGetName(thisObject, deviceNameChars); + if (nameResult != KERN_SUCCESS) { + NSLog(CFSTR("Could not get name for FireWire object: IORegistryEntryGetName returned 0x%x"), nameResult); + return NULL; + } + CFStringRef tempDeviceName = CFStringCreateWithCString(kCFAllocatorDefault, + deviceNameChars, + kCFStringEncodingASCII); + if (tempDeviceName) { + if (CFStringCompare(tempDeviceName, CFSTR("IOFireWireDevice"), 0) != kCFCompareEqualTo) + return tempDeviceName; + else + CFRelease(tempDeviceName); + } + + tempDeviceName = IORegistryEntrySearchCFProperty(thisObject, + kIOFireWirePlane, + CFSTR("FireWire Product Name"), + nil, + kIORegistryIterateRecursively); + + if (tempDeviceName) + return tempDeviceName; + + tempDeviceName = IORegistryEntrySearchCFProperty(thisObject, + kIOFireWirePlane, + CFSTR("FireWire Vendor Name"), + nil, + kIORegistryIterateRecursively); + + if (tempDeviceName) + return tempDeviceName; + + return CFCopyLocalizedString(CFSTR("Unnamed FireWire Device"), ""); +} + +#pragma mark Callbacks + +static void fwDeviceAdded(void *refCon, io_iterator_t iterator) { +#pragma unused(refCon) +// NSLog(@"FireWire Device Added Notification."); + io_object_t thisObject; + while ((thisObject = IOIteratorNext(iterator))) { + Boolean keyExistsAndHasValidFormat; + if (notificationsArePrimed || CFPreferencesGetAppBooleanValue(CFSTR("ShowExisting"), CFSTR("com.growl.hardwaregrowler"), &keyExistsAndHasValidFormat)) { + CFStringRef deviceName = nameForFireWireObject(thisObject); + // NSLog(@"FireWire Device Attached: %@" , deviceName); + AppController_fwDidConnect(deviceName); + CFRelease(deviceName); + } + IOObjectRelease(thisObject); + } +} + +static void fwDeviceRemoved(void *refCon, io_iterator_t iterator) { +#pragma unused(refCon) +// NSLog(@"FireWire Device Removed Notification."); + io_object_t thisObject; + while ((thisObject = IOIteratorNext(iterator))) { + CFStringRef deviceName = nameForFireWireObject(thisObject); + // NSLog(@"FireWire Device Removed: %@" , deviceName); + AppController_fwDidDisconnect(deviceName); + CFRelease(deviceName); + + IOObjectRelease(thisObject); + } +} + +#pragma mark - + +static void registerForFireWireNotifications(void) { + //http://developer.apple.com/documentation/DeviceDrivers/Conceptual/AccessingHardware/AH_Finding_Devices/chapter_4_section_2.html#//apple_ref/doc/uid/TP30000379/BABEACCJ + kern_return_t matchingResult; + io_iterator_t addedIterator; + kern_return_t removeNoteResult; + io_iterator_t removedIterator; + CFDictionaryRef myFireWireMatchDictionary; + +// NSLog(@"registerForFireWireNotifications"); + + // Setup a matching dictionary. + myFireWireMatchDictionary = IOServiceMatching("IOFireWireDevice"); + + // Register our notification + matchingResult = IOServiceAddMatchingNotification(ioKitNotificationPort, + kIOPublishNotification, + myFireWireMatchDictionary, + fwDeviceAdded, + NULL, + &addedIterator); + + if (matchingResult) + NSLog(CFSTR("matching notification registration failed: %d)"), matchingResult); + + // Prime the notifications (And deal with the existing devices)... + fwDeviceAdded(NULL, addedIterator); + + // Register for removal notifications. + + // It seems we have to make a new dictionary... reusing the old one didn't work. + myFireWireMatchDictionary = IOServiceMatching("IOFireWireDevice"); + removeNoteResult = IOServiceAddMatchingNotification(ioKitNotificationPort, + kIOTerminatedNotification, + myFireWireMatchDictionary, + fwDeviceRemoved, + NULL, + &removedIterator); + + // Matching notification must be "primed" by iterating over the + // iterator returned from IOServiceAddMatchingNotification(), so + // we call our device removed method here... + // + if (kIOReturnSuccess != removeNoteResult) + NSLog(CFSTR("Couldn't add device removal notification")); + else + fwDeviceRemoved(NULL, removedIterator); + + notificationsArePrimed = true; +} + +#pragma mark - + +void FireWireNotifier_init(void) { + notificationsArePrimed = false; +//#warning kIOMasterPortDefault is only available on 10.2 and above... + ioKitNotificationPort = IONotificationPortCreate(kIOMasterPortDefault); + notificationRunLoopSource = IONotificationPortGetRunLoopSource(ioKitNotificationPort); + + CFRunLoopAddSource(CFRunLoopGetCurrent(), + notificationRunLoopSource, + kCFRunLoopDefaultMode); + registerForFireWireNotifications(); +} + +void FireWireNotifier_dealloc(void) { + if (ioKitNotificationPort) { + CFRunLoopRemoveSource(CFRunLoopGetCurrent(), notificationRunLoopSource, kCFRunLoopDefaultMode); + IONotificationPortDestroy(ioKitNotificationPort); + } +} diff --git a/FireWireNotifier.h b/FireWireNotifier.h new file mode 100644 index 0000000..b129940 --- /dev/null +++ b/FireWireNotifier.h @@ -0,0 +1,9 @@ +/* FireWireNotifier */ + +#ifndef FIREWIRE_NOTIFIER_H +#define FIREWIRE_NOTIFIER_H + +void FireWireNotifier_init(void); +void FireWireNotifier_dealloc(void); + +#endif diff --git a/HardwareGrowler.xcodeproj/project.pbxproj b/HardwareGrowler.xcodeproj/project.pbxproj new file mode 100644 index 0000000..a6da1c1 --- /dev/null +++ b/HardwareGrowler.xcodeproj/project.pbxproj @@ -0,0 +1,482 @@ +// !$*UTF8*$! +{ + archiveVersion = 1; + classes = { + }; + objectVersion = 46; + objects = { + +/* Begin PBXBuildFile section */ + 179B92900C14D110004FB187 /* VolumeNotifier.m in Sources */ = {isa = PBXBuildFile; fileRef = 179B928F0C14D110004FB187 /* VolumeNotifier.m */; }; + 3423607209D8888A009FF634 /* PowerNotifier.c in Sources */ = {isa = PBXBuildFile; fileRef = 3423607009D8888A009FF634 /* PowerNotifier.c */; }; + 345BD6330C8B12B8005C3DA2 /* NetworkNotifier.m in Sources */ = {isa = PBXBuildFile; fileRef = 345BD6320C8B12B8005C3DA2 /* NetworkNotifier.m */; }; + 8D11072A0486CEB800E47090 /* MainMenu.nib in Resources */ = {isa = PBXBuildFile; fileRef = 29B97318FDCFA39411CA2CEA /* MainMenu.nib */; }; + 8D11072B0486CEB800E47090 /* InfoPlist.strings in Resources */ = {isa = PBXBuildFile; fileRef = 089C165CFE840E0CC02AAC07 /* InfoPlist.strings */; }; + 8D11072D0486CEB800E47090 /* main.c in Sources */ = {isa = PBXBuildFile; fileRef = 29B97316FDCFA39411CA2CEA /* main.c */; settings = {ATTRIBUTES = (); }; }; + 8D11072F0486CEB800E47090 /* Cocoa.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 1058C7A1FEA54F0111CA2CBB /* Cocoa.framework */; }; + 9586EF0408C9C3A40039789E /* SyncNotifier.c in Sources */ = {isa = PBXBuildFile; fileRef = 9586EF0208C9C3A40039789E /* SyncNotifier.c */; }; + 958AB9DE07C6A7FD00F56B45 /* SystemConfiguration.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 958AB9DD07C6A7FD00F56B45 /* SystemConfiguration.framework */; }; + 95AC163C0858A3BF008213B8 /* Localizable.strings in Resources */ = {isa = PBXBuildFile; fileRef = 95AC163A0858A3BF008213B8 /* Localizable.strings */; }; + DD1E113F07C7558B005BD5FC /* hwGrowlerIcon.icns in Resources */ = {isa = PBXBuildFile; fileRef = DD1E113E07C7558B005BD5FC /* hwGrowlerIcon.icns */; }; + DD1E135C07C923FD005BD5FC /* Credits.rtf in Resources */ = {isa = PBXBuildFile; fileRef = DD1E135B07C923FD005BD5FC /* Credits.rtf */; }; + DD54997107BA5E420010BE54 /* AppController.m in Sources */ = {isa = PBXBuildFile; fileRef = DD54996F07BA5E420010BE54 /* AppController.m */; }; + DD54999B07BA61650010BE54 /* IOKit.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = DD54999A07BA61650010BE54 /* IOKit.framework */; }; + DDE55D2807BAEDE2000631AC /* FireWireNotifier.c in Sources */ = {isa = PBXBuildFile; fileRef = DDE55D2607BAEDE2000631AC /* FireWireNotifier.c */; }; + DDF9B2B007BBD4600007B5B7 /* USBNotifier.c in Sources */ = {isa = PBXBuildFile; fileRef = DDF9B2AE07BBD4600007B5B7 /* USBNotifier.c */; }; + DDF9B2FE07BBD9020007B5B7 /* BluetoothNotifier.c in Sources */ = {isa = PBXBuildFile; fileRef = DDF9B2FC07BBD9020007B5B7 /* BluetoothNotifier.c */; }; + DDF9B33A07BBDAA40007B5B7 /* IOBluetooth.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = DDF9B33907BBDAA40007B5B7 /* IOBluetooth.framework */; }; + DDF9B81107BC20630007B5B7 /* readme.txt in Resources */ = {isa = PBXBuildFile; fileRef = DDF9B81007BC20630007B5B7 /* readme.txt */; }; +/* End PBXBuildFile section */ + +/* Begin PBXCopyFilesBuildPhase section */ + 9324AE8407BD780A001CB55C /* CopyFiles */ = { + isa = PBXCopyFilesBuildPhase; + buildActionMask = 2147483647; + dstPath = ""; + dstSubfolderSpec = 10; + files = ( + ); + runOnlyForDeploymentPostprocessing = 0; + }; +/* End PBXCopyFilesBuildPhase section */ + +/* Begin PBXFileReference section */ + 089C165DFE840E0CC02AAC07 /* English */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.strings; name = English; path = English.lproj/InfoPlist.strings; sourceTree = ""; }; + 1058C7A1FEA54F0111CA2CBB /* Cocoa.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = Cocoa.framework; path = /System/Library/Frameworks/Cocoa.framework; sourceTree = ""; }; + 179B928F0C14D110004FB187 /* VolumeNotifier.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = VolumeNotifier.m; sourceTree = ""; }; + 29B97316FDCFA39411CA2CEA /* main.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = main.c; sourceTree = ""; }; + 29B97319FDCFA39411CA2CEA /* English */ = {isa = PBXFileReference; lastKnownFileType = wrapper.nib; name = English; path = English.lproj/MainMenu.nib; sourceTree = ""; }; + 29B97324FDCFA39411CA2CEA /* AppKit.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = AppKit.framework; path = /System/Library/Frameworks/AppKit.framework; sourceTree = ""; }; + 29B97325FDCFA39411CA2CEA /* Foundation.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = Foundation.framework; path = /System/Library/Frameworks/Foundation.framework; sourceTree = ""; }; + 32CA4F630368D1EE00C91783 /* HardwareNotifier_Prefix.pch */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = HardwareNotifier_Prefix.pch; sourceTree = ""; }; + 3423606F09D8888A009FF634 /* PowerNotifier.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = PowerNotifier.h; sourceTree = ""; }; + 3423607009D8888A009FF634 /* PowerNotifier.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = PowerNotifier.c; sourceTree = ""; }; + 345BD6320C8B12B8005C3DA2 /* NetworkNotifier.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = NetworkNotifier.m; sourceTree = ""; }; + 34CD98C90C87CA7F0040C77F /* pt_BR */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.strings; name = pt_BR; path = pt_BR.lproj/InfoPlist.strings; sourceTree = ""; }; + 34CD98CA0C87CA7F0040C77F /* pt_BR */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.strings; name = pt_BR; path = pt_BR.lproj/Localizable.strings; sourceTree = ""; }; + 34CD98CB0C87CA7F0040C77F /* pt_BR */ = {isa = PBXFileReference; lastKnownFileType = wrapper.nib; name = pt_BR; path = pt_BR.lproj/MainMenu.nib; sourceTree = ""; }; + 8D1107310486CEB800E47090 /* Info.plist */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist; path = Info.plist; sourceTree = ""; }; + 8D1107320486CEB800E47090 /* HardwareGrowler.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = HardwareGrowler.app; sourceTree = BUILT_PRODUCTS_DIR; }; + 9586EF0108C9C3A40039789E /* SyncNotifier.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = SyncNotifier.h; sourceTree = ""; }; + 9586EF0208C9C3A40039789E /* SyncNotifier.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = SyncNotifier.c; sourceTree = ""; }; + 958AB93507C69DE000F56B45 /* NetworkNotifier.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = NetworkNotifier.h; sourceTree = ""; }; + 958AB9DD07C6A7FD00F56B45 /* SystemConfiguration.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = SystemConfiguration.framework; path = /System/Library/Frameworks/SystemConfiguration.framework; sourceTree = ""; }; + 95AC163B0858A3BF008213B8 /* English */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.strings; name = English; path = English.lproj/Localizable.strings; sourceTree = ""; }; + 95B00A8C08468A62004A8623 /* zh_CN */ = {isa = PBXFileReference; lastKnownFileType = wrapper.nib; name = zh_CN; path = zh_CN.lproj/MainMenu.nib; sourceTree = ""; }; + DD1E113E07C7558B005BD5FC /* hwGrowlerIcon.icns */ = {isa = PBXFileReference; lastKnownFileType = image.icns; path = hwGrowlerIcon.icns; sourceTree = ""; }; + DD1E135B07C923FD005BD5FC /* Credits.rtf */ = {isa = PBXFileReference; lastKnownFileType = text.rtf; name = Credits.rtf; path = English.lproj/Credits.rtf; sourceTree = ""; }; + DD54996E07BA5E420010BE54 /* AppController.h */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = sourcecode.c.h; path = AppController.h; sourceTree = ""; }; + DD54996F07BA5E420010BE54 /* AppController.m */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = sourcecode.c.objc; path = AppController.m; sourceTree = ""; }; + DD54999A07BA61650010BE54 /* IOKit.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = IOKit.framework; path = /System/Library/Frameworks/IOKit.framework; sourceTree = ""; }; + DDE55D2507BAEDE2000631AC /* FireWireNotifier.h */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = sourcecode.c.h; path = FireWireNotifier.h; sourceTree = ""; }; + DDE55D2607BAEDE2000631AC /* FireWireNotifier.c */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = sourcecode.c.c; path = FireWireNotifier.c; sourceTree = ""; }; + DDF9B2AD07BBD4600007B5B7 /* USBNotifier.h */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = sourcecode.c.h; path = USBNotifier.h; sourceTree = ""; }; + DDF9B2AE07BBD4600007B5B7 /* USBNotifier.c */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = sourcecode.c.c; path = USBNotifier.c; sourceTree = ""; }; + DDF9B2FC07BBD9020007B5B7 /* BluetoothNotifier.c */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = sourcecode.c.c; path = BluetoothNotifier.c; sourceTree = ""; }; + DDF9B2FD07BBD9020007B5B7 /* BluetoothNotifier.h */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = sourcecode.c.h; path = BluetoothNotifier.h; sourceTree = ""; }; + DDF9B33907BBDAA40007B5B7 /* IOBluetooth.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = IOBluetooth.framework; path = /System/Library/Frameworks/IOBluetooth.framework; sourceTree = ""; }; + DDF9B63707BC07B30007B5B7 /* VolumeNotifier.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = VolumeNotifier.h; sourceTree = ""; }; + DDF9B81007BC20630007B5B7 /* readme.txt */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = text; path = readme.txt; sourceTree = ""; }; +/* End PBXFileReference section */ + +/* Begin PBXFrameworksBuildPhase section */ + 8D11072E0486CEB800E47090 /* Frameworks */ = { + isa = PBXFrameworksBuildPhase; + buildActionMask = 2147483647; + files = ( + 8D11072F0486CEB800E47090 /* Cocoa.framework in Frameworks */, + DD54999B07BA61650010BE54 /* IOKit.framework in Frameworks */, + DDF9B33A07BBDAA40007B5B7 /* IOBluetooth.framework in Frameworks */, + 958AB9DE07C6A7FD00F56B45 /* SystemConfiguration.framework in Frameworks */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; +/* End PBXFrameworksBuildPhase section */ + +/* Begin PBXGroup section */ + 080E96DDFE201D6D7F000001 /* Classes */ = { + isa = PBXGroup; + children = ( + DD54996E07BA5E420010BE54 /* AppController.h */, + DD54996F07BA5E420010BE54 /* AppController.m */, + DDE55D2507BAEDE2000631AC /* FireWireNotifier.h */, + DDE55D2607BAEDE2000631AC /* FireWireNotifier.c */, + DDF9B2AD07BBD4600007B5B7 /* USBNotifier.h */, + DDF9B2AE07BBD4600007B5B7 /* USBNotifier.c */, + DDF9B2FD07BBD9020007B5B7 /* BluetoothNotifier.h */, + DDF9B2FC07BBD9020007B5B7 /* BluetoothNotifier.c */, + DDF9B63707BC07B30007B5B7 /* VolumeNotifier.h */, + 179B928F0C14D110004FB187 /* VolumeNotifier.m */, + 958AB93507C69DE000F56B45 /* NetworkNotifier.h */, + 345BD6320C8B12B8005C3DA2 /* NetworkNotifier.m */, + 9586EF0108C9C3A40039789E /* SyncNotifier.h */, + 9586EF0208C9C3A40039789E /* SyncNotifier.c */, + 3423606F09D8888A009FF634 /* PowerNotifier.h */, + 3423607009D8888A009FF634 /* PowerNotifier.c */, + ); + name = Classes; + sourceTree = ""; + }; + 1058C7A0FEA54F0111CA2CBB /* Linked Frameworks */ = { + isa = PBXGroup; + children = ( + 958AB9DD07C6A7FD00F56B45 /* SystemConfiguration.framework */, + 1058C7A1FEA54F0111CA2CBB /* Cocoa.framework */, + DDF9B33907BBDAA40007B5B7 /* IOBluetooth.framework */, + DD54999A07BA61650010BE54 /* IOKit.framework */, + ); + name = "Linked Frameworks"; + sourceTree = ""; + }; + 1058C7A2FEA54F0111CA2CBB /* Other Frameworks */ = { + isa = PBXGroup; + children = ( + 29B97325FDCFA39411CA2CEA /* Foundation.framework */, + 29B97324FDCFA39411CA2CEA /* AppKit.framework */, + ); + name = "Other Frameworks"; + sourceTree = ""; + }; + 19C28FACFE9D520D11CA2CBB /* Products */ = { + isa = PBXGroup; + children = ( + 8D1107320486CEB800E47090 /* HardwareGrowler.app */, + ); + name = Products; + sourceTree = ""; + }; + 29B97314FDCFA39411CA2CEA /* HarwareNotifier */ = { + isa = PBXGroup; + children = ( + 080E96DDFE201D6D7F000001 /* Classes */, + 29B97315FDCFA39411CA2CEA /* Other Sources */, + 29B97317FDCFA39411CA2CEA /* Resources */, + 29B97323FDCFA39411CA2CEA /* Frameworks */, + 19C28FACFE9D520D11CA2CBB /* Products */, + ); + name = HarwareNotifier; + sourceTree = ""; + }; + 29B97315FDCFA39411CA2CEA /* Other Sources */ = { + isa = PBXGroup; + children = ( + 32CA4F630368D1EE00C91783 /* HardwareNotifier_Prefix.pch */, + 29B97316FDCFA39411CA2CEA /* main.c */, + ); + name = "Other Sources"; + sourceTree = ""; + }; + 29B97317FDCFA39411CA2CEA /* Resources */ = { + isa = PBXGroup; + children = ( + 95AC163A0858A3BF008213B8 /* Localizable.strings */, + DDF9B81007BC20630007B5B7 /* readme.txt */, + DD1E135B07C923FD005BD5FC /* Credits.rtf */, + DD1E113E07C7558B005BD5FC /* hwGrowlerIcon.icns */, + 8D1107310486CEB800E47090 /* Info.plist */, + 089C165CFE840E0CC02AAC07 /* InfoPlist.strings */, + 29B97318FDCFA39411CA2CEA /* MainMenu.nib */, + ); + name = Resources; + sourceTree = ""; + }; + 29B97323FDCFA39411CA2CEA /* Frameworks */ = { + isa = PBXGroup; + children = ( + 1058C7A0FEA54F0111CA2CBB /* Linked Frameworks */, + 1058C7A2FEA54F0111CA2CBB /* Other Frameworks */, + ); + name = Frameworks; + sourceTree = ""; + }; +/* End PBXGroup section */ + +/* Begin PBXNativeTarget section */ + 8D1107260486CEB800E47090 /* HardwareGrowler */ = { + isa = PBXNativeTarget; + buildConfigurationList = 95E4A2B50855C0DB006B3F45 /* Build configuration list for PBXNativeTarget "HardwareGrowler" */; + buildPhases = ( + 8D1107290486CEB800E47090 /* Resources */, + 8D11072C0486CEB800E47090 /* Sources */, + 8D11072E0486CEB800E47090 /* Frameworks */, + 9324AE8407BD780A001CB55C /* CopyFiles */, + ); + buildRules = ( + ); + comments = "Rawr!!..\n\n"; + dependencies = ( + ); + name = HardwareGrowler; + productInstallPath = "$(HOME)/Applications"; + productName = HarwareNotifier; + productReference = 8D1107320486CEB800E47090 /* HardwareGrowler.app */; + productType = "com.apple.product-type.application"; + }; +/* End PBXNativeTarget section */ + +/* Begin PBXProject section */ + 29B97313FDCFA39411CA2CEA /* Project object */ = { + isa = PBXProject; + attributes = { + LastUpgradeCheck = 0440; + }; + buildConfigurationList = 95E4A2BA0855C0DB006B3F45 /* Build configuration list for PBXProject "HardwareGrowler" */; + compatibilityVersion = "Xcode 3.2"; + developmentRegion = English; + hasScannedForEncodings = 1; + knownRegions = ( + English, + "pt-BR", + "zh-Hans", + en, + ); + mainGroup = 29B97314FDCFA39411CA2CEA /* HarwareNotifier */; + projectDirPath = ""; + projectRoot = ""; + targets = ( + 8D1107260486CEB800E47090 /* HardwareGrowler */, + ); + }; +/* End PBXProject section */ + +/* Begin PBXResourcesBuildPhase section */ + 8D1107290486CEB800E47090 /* Resources */ = { + isa = PBXResourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + 8D11072A0486CEB800E47090 /* MainMenu.nib in Resources */, + 8D11072B0486CEB800E47090 /* InfoPlist.strings in Resources */, + DDF9B81107BC20630007B5B7 /* readme.txt in Resources */, + DD1E113F07C7558B005BD5FC /* hwGrowlerIcon.icns in Resources */, + DD1E135C07C923FD005BD5FC /* Credits.rtf in Resources */, + 95AC163C0858A3BF008213B8 /* Localizable.strings in Resources */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; +/* End PBXResourcesBuildPhase section */ + +/* Begin PBXSourcesBuildPhase section */ + 8D11072C0486CEB800E47090 /* Sources */ = { + isa = PBXSourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + 8D11072D0486CEB800E47090 /* main.c in Sources */, + DD54997107BA5E420010BE54 /* AppController.m in Sources */, + DDE55D2807BAEDE2000631AC /* FireWireNotifier.c in Sources */, + DDF9B2B007BBD4600007B5B7 /* USBNotifier.c in Sources */, + DDF9B2FE07BBD9020007B5B7 /* BluetoothNotifier.c in Sources */, + 9586EF0408C9C3A40039789E /* SyncNotifier.c in Sources */, + 3423607209D8888A009FF634 /* PowerNotifier.c in Sources */, + 179B92900C14D110004FB187 /* VolumeNotifier.m in Sources */, + 345BD6330C8B12B8005C3DA2 /* NetworkNotifier.m in Sources */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; +/* End PBXSourcesBuildPhase section */ + +/* Begin PBXVariantGroup section */ + 089C165CFE840E0CC02AAC07 /* InfoPlist.strings */ = { + isa = PBXVariantGroup; + children = ( + 089C165DFE840E0CC02AAC07 /* English */, + ); + name = InfoPlist.strings; + sourceTree = ""; + }; + 29B97318FDCFA39411CA2CEA /* MainMenu.nib */ = { + isa = PBXVariantGroup; + children = ( + 29B97319FDCFA39411CA2CEA /* English */, + ); + name = MainMenu.nib; + sourceTree = ""; + }; + 95AC163A0858A3BF008213B8 /* Localizable.strings */ = { + isa = PBXVariantGroup; + children = ( + 95AC163B0858A3BF008213B8 /* English */, + ); + name = Localizable.strings; + sourceTree = ""; + }; +/* End PBXVariantGroup section */ + +/* Begin XCBuildConfiguration section */ + 95E4A2B60855C0DB006B3F45 /* Debug */ = { + isa = XCBuildConfiguration; + buildSettings = { + COMBINE_HIDPI_IMAGES = YES; + FRAMEWORK_SEARCH_PATHS = ""; + GCC_C_LANGUAGE_STANDARD = gnu99; + GCC_DYNAMIC_NO_PIC = NO; + GCC_ENABLE_SYMBOL_SEPARATION = NO; + GCC_OPTIMIZATION_LEVEL = 0; + GCC_PRECOMPILE_PREFIX_HEADER = YES; + GCC_PREFIX_HEADER = HardwareNotifier_Prefix.pch; + GCC_VERSION = ""; + GCC_WARN_64_TO_32_BIT_CONVERSION = YES; + GCC_WARN_ABOUT_MISSING_NEWLINE = YES; + GCC_WARN_ABOUT_MISSING_PROTOTYPES = YES; + GCC_WARN_ABOUT_RETURN_TYPE = YES; + GCC_WARN_CHECK_SWITCH_STATEMENTS = YES; + GCC_WARN_INITIALIZER_NOT_FULLY_BRACKETED = YES; + GCC_WARN_MISSING_PARENTHESES = YES; + GCC_WARN_SHADOW = YES; + GCC_WARN_SIGN_COMPARE = YES; + GCC_WARN_TYPECHECK_CALLS_TO_PRINTF = YES; + GCC_WARN_UNINITIALIZED_AUTOS = NO; + GCC_WARN_UNUSED_FUNCTION = YES; + GCC_WARN_UNUSED_LABEL = YES; + GCC_WARN_UNUSED_VALUE = YES; + GCC_WARN_UNUSED_VARIABLE = YES; + INFOPLIST_FILE = Info.plist; + INFOPLIST_OTHER_PREPROCESSOR_FLAGS = ""; + INFOPLIST_PREFIX_HEADER = ""; + INSTALL_PATH = "$(HOME)/Applications"; + PRECOMPS_INCLUDE_HEADERS_FROM_BUILT_PRODUCTS_DIR = NO; + PRODUCT_NAME = HardwareGrowler; + WARNING_CFLAGS = ( + "-W", + "-Wall", + ); + WRAPPER_EXTENSION = app; + ZERO_LINK = NO; + }; + name = Debug; + }; + 95E4A2B70855C0DB006B3F45 /* Release */ = { + isa = XCBuildConfiguration; + buildSettings = { + COMBINE_HIDPI_IMAGES = YES; + DEAD_CODE_STRIPPING = YES; + FRAMEWORK_SEARCH_PATHS = ""; + GCC_C_LANGUAGE_STANDARD = gnu99; + GCC_DEBUGGING_SYMBOLS = full; + GCC_PRECOMPILE_PREFIX_HEADER = YES; + GCC_PREFIX_HEADER = HardwareNotifier_Prefix.pch; + GCC_VERSION = ""; + GCC_WARN_64_TO_32_BIT_CONVERSION = YES; + GCC_WARN_ABOUT_MISSING_NEWLINE = YES; + GCC_WARN_ABOUT_MISSING_PROTOTYPES = YES; + GCC_WARN_ABOUT_RETURN_TYPE = YES; + GCC_WARN_CHECK_SWITCH_STATEMENTS = YES; + GCC_WARN_INITIALIZER_NOT_FULLY_BRACKETED = YES; + GCC_WARN_MISSING_PARENTHESES = YES; + GCC_WARN_SHADOW = YES; + GCC_WARN_SIGN_COMPARE = YES; + GCC_WARN_TYPECHECK_CALLS_TO_PRINTF = YES; + GCC_WARN_UNINITIALIZED_AUTOS = YES; + GCC_WARN_UNKNOWN_PRAGMAS = YES; + GCC_WARN_UNUSED_FUNCTION = YES; + GCC_WARN_UNUSED_LABEL = YES; + GCC_WARN_UNUSED_PARAMETER = YES; + GCC_WARN_UNUSED_VALUE = YES; + GCC_WARN_UNUSED_VARIABLE = YES; + INFOPLIST_FILE = Info.plist; + INFOPLIST_OTHER_PREPROCESSOR_FLAGS = ""; + INFOPLIST_PREFIX_HEADER = ""; + INSTALL_PATH = "$(HOME)/Applications"; + PRODUCT_NAME = HardwareGrowler; + WARNING_CFLAGS = ( + "-W", + "-Wall", + ); + WRAPPER_EXTENSION = app; + ZERO_LINK = NO; + }; + name = Release; + }; + 95E4A2BB0855C0DB006B3F45 /* Debug */ = { + isa = XCBuildConfiguration; + buildSettings = { + ARCHS = "$(ARCHS_STANDARD_64_BIT)"; + DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; + GCC_GENERATE_DEBUGGING_SYMBOLS = YES; + GCC_VERSION = ""; + GCC_WARN_64_TO_32_BIT_CONVERSION = YES; + GCC_WARN_ABOUT_MISSING_FIELD_INITIALIZERS = YES; + GCC_WARN_ABOUT_MISSING_NEWLINE = YES; + GCC_WARN_ABOUT_MISSING_PROTOTYPES = YES; + GCC_WARN_CHECK_SWITCH_STATEMENTS = YES; + GCC_WARN_INITIALIZER_NOT_FULLY_BRACKETED = YES; + GCC_WARN_MISSING_PARENTHESES = YES; + GCC_WARN_SHADOW = YES; + GCC_WARN_SIGN_COMPARE = YES; + GCC_WARN_UNDECLARED_SELECTOR = YES; + GCC_WARN_UNKNOWN_PRAGMAS = YES; + GCC_WARN_UNUSED_FUNCTION = YES; + GCC_WARN_UNUSED_LABEL = YES; + GCC_WARN_UNUSED_VALUE = YES; + GCC_WARN_UNUSED_VARIABLE = YES; + INFOPLIST_OTHER_PREPROCESSOR_FLAGS = "-traditional -CC -I \"../../build/$(CONFIGURATION)/include\" -I \"$(TARGET_BUILD_DIR)/include\" "; + INFOPLIST_PREFIX_HEADER = "../../Info.plist-Prefix.pch"; + INFOPLIST_PREPROCESS = YES; + MACOSX_DEPLOYMENT_TARGET = 10.8; + RUN_CLANG_STATIC_ANALYZER = YES; + SDKROOT = ""; + VALID_ARCHS = x86_64; + }; + name = Debug; + }; + 95E4A2BC0855C0DB006B3F45 /* Release */ = { + isa = XCBuildConfiguration; + buildSettings = { + ARCHS = "$(ARCHS_STANDARD_64_BIT)"; + COPY_PHASE_STRIP = NO; + DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; + DEPLOYMENT_POSTPROCESSING = YES; + GCC_GENERATE_DEBUGGING_SYMBOLS = YES; + GCC_VERSION = ""; + GCC_WARN_64_TO_32_BIT_CONVERSION = YES; + GCC_WARN_ABOUT_MISSING_FIELD_INITIALIZERS = YES; + GCC_WARN_ABOUT_MISSING_NEWLINE = YES; + GCC_WARN_ABOUT_MISSING_PROTOTYPES = YES; + GCC_WARN_CHECK_SWITCH_STATEMENTS = YES; + GCC_WARN_INITIALIZER_NOT_FULLY_BRACKETED = YES; + GCC_WARN_MISSING_PARENTHESES = YES; + GCC_WARN_SHADOW = YES; + GCC_WARN_SIGN_COMPARE = YES; + GCC_WARN_UNDECLARED_SELECTOR = YES; + GCC_WARN_UNINITIALIZED_AUTOS = YES; + GCC_WARN_UNKNOWN_PRAGMAS = YES; + GCC_WARN_UNUSED_FUNCTION = YES; + GCC_WARN_UNUSED_LABEL = YES; + GCC_WARN_UNUSED_VALUE = YES; + GCC_WARN_UNUSED_VARIABLE = YES; + INFOPLIST_OTHER_PREPROCESSOR_FLAGS = "-traditional -CC -I \"../../build/$(CONFIGURATION)/include\" -I \"$(TARGET_BUILD_DIR)/include\" "; + INFOPLIST_PREFIX_HEADER = "../../Info.plist-Prefix.pch"; + INFOPLIST_PREPROCESS = YES; + MACOSX_DEPLOYMENT_TARGET = 10.8; + RUN_CLANG_STATIC_ANALYZER = YES; + SDKROOT = ""; + SEPARATE_STRIP = YES; + STRIP_INSTALLED_PRODUCT = YES; + VALID_ARCHS = x86_64; + }; + name = Release; + }; +/* End XCBuildConfiguration section */ + +/* Begin XCConfigurationList section */ + 95E4A2B50855C0DB006B3F45 /* Build configuration list for PBXNativeTarget "HardwareGrowler" */ = { + isa = XCConfigurationList; + buildConfigurations = ( + 95E4A2B60855C0DB006B3F45 /* Debug */, + 95E4A2B70855C0DB006B3F45 /* Release */, + ); + defaultConfigurationIsVisible = 0; + defaultConfigurationName = Debug; + }; + 95E4A2BA0855C0DB006B3F45 /* Build configuration list for PBXProject "HardwareGrowler" */ = { + isa = XCConfigurationList; + buildConfigurations = ( + 95E4A2BB0855C0DB006B3F45 /* Debug */, + 95E4A2BC0855C0DB006B3F45 /* Release */, + ); + defaultConfigurationIsVisible = 0; + defaultConfigurationName = Debug; + }; +/* End XCConfigurationList section */ + }; + rootObject = 29B97313FDCFA39411CA2CEA /* Project object */; +} diff --git a/HardwareGrowler.xcodeproj/project.xcworkspace/contents.xcworkspacedata b/HardwareGrowler.xcodeproj/project.xcworkspace/contents.xcworkspacedata new file mode 100644 index 0000000..60cc3fd --- /dev/null +++ b/HardwareGrowler.xcodeproj/project.xcworkspace/contents.xcworkspacedata @@ -0,0 +1,7 @@ + + + + + diff --git a/HardwareNotifier_Prefix.pch b/HardwareNotifier_Prefix.pch new file mode 100644 index 0000000..1fa429e --- /dev/null +++ b/HardwareNotifier_Prefix.pch @@ -0,0 +1,7 @@ +// +// Prefix header for all source files of the 'HardwareGrowler' target in the 'HardwareGrowler' project +// + +#ifdef __OBJC__ + #import +#endif diff --git a/Info.plist b/Info.plist new file mode 100644 index 0000000..8e449b1 --- /dev/null +++ b/Info.plist @@ -0,0 +1,24 @@ + + + + + CFBundleDevelopmentRegion + English + CFBundleExecutable + HardwareGrowler + CFBundleIconFile + hwGrowlerIcon + CFBundleIdentifier + org.cirrus.hardwaregrowler + CFBundleInfoDictionaryVersion + 6.0 + CFBundlePackageType + APPL + CFBundleSignature + HwGr + NSMainNibFile + MainMenu + NSPrincipalClass + NSApplication + + diff --git a/Makefile b/Makefile new file mode 100644 index 0000000..951a6af --- /dev/null +++ b/Makefile @@ -0,0 +1,9 @@ +DEFAULT_BUILDCONFIGURATION=Deployment + +BUILDCONFIGURATION?=$(DEFAULT_BUILDCONFIGURATION) + +all: + xcodebuild -alltargets -configuration $(BUILDCONFIGURATION) build + +clean: + xcodebuild -alltargets clean diff --git a/NetworkNotifier.h b/NetworkNotifier.h new file mode 100644 index 0000000..0eab575 --- /dev/null +++ b/NetworkNotifier.h @@ -0,0 +1,14 @@ +// +// NetworkNotifier.h +// HardwareGrowler +// +// Created by Ingmar Stein on 18.02.05. +// Copyright 2005 The Growl Project. All rights reserved. +// + +@interface NetworkNotifier : NSObject +{ + +} + +@end diff --git a/NetworkNotifier.m b/NetworkNotifier.m new file mode 100644 index 0000000..3469351 --- /dev/null +++ b/NetworkNotifier.m @@ -0,0 +1,320 @@ +// +// NetworkNotifier.c +// HardwareGrowler +// +// Created by Ingmar Stein on 18.02.05. +// Copyright 2005 The Growl Project. All rights reserved. +// Copyright (C) 2004 Scott Lamb +// + +#import + +#include "NetworkNotifier.h" +#include "AppController.h" +#include + +// Media stuff +#include +#include +#include +#include +#include +#include +#include +#include + +/* @"Link Status" == 1 seems to mean disconnected */ +#define AIRPORT_DISCONNECTED 1 + +static CFDictionaryRef airportStatus; + +/** A reference to the SystemConfiguration dynamic store. */ +static SCDynamicStoreRef dynStore; + +/** Our run loop source for notification. */ +static CFRunLoopSourceRef rlSrc; + +static struct ifmedia_description ifm_subtype_ethernet_descriptions[] = IFM_SUBTYPE_ETHERNET_DESCRIPTIONS; +static struct ifmedia_description ifm_shared_option_descriptions[] = IFM_SHARED_OPTION_DESCRIPTIONS; + +@implementation NetworkNotifier + +- (NSString *)getMediaForInterface:(const char *)interface { + // This is all made by looking through Darwin's src/network_cmds/ifconfig.tproj. + // There's no pretty way to get media stuff; I've stripped it down to the essentials + // for what I'm doing. + + size_t length = strlen(interface); + if (length >= IFNAMSIZ) + NSLog(@"Interface name too long"); + + int s = socket(AF_INET, SOCK_DGRAM, 0); + if (s < 0) { + NSLog(@"Can't open datagram socket"); + return NULL; + } + struct ifmediareq ifmr; + memset(&ifmr, 0, sizeof(ifmr)); + strncpy(ifmr.ifm_name, interface, sizeof(ifmr.ifm_name)); + + if (ioctl(s, SIOCGIFMEDIA, (caddr_t)&ifmr) < 0) { + // Media not supported. + close(s); + return NULL; + } + + close(s); + + // Now ifmr.ifm_current holds the selected type (probably auto-select) + // ifmr.ifm_active holds details (100baseT or similar) + // We only want the ifm_active bit. + + const char *type = "Unknown"; + + // We'll only look in the Ethernet list. I don't care about anything else. + struct ifmedia_description *desc; + for (desc = ifm_subtype_ethernet_descriptions; desc->ifmt_string; ++desc) { + if (IFM_SUBTYPE(ifmr.ifm_active) == desc->ifmt_word) { + type = desc->ifmt_string; + break; + } + } + + NSMutableString *options = nil; + + // And fill in the duplex settings. + for (desc = ifm_shared_option_descriptions; desc->ifmt_string; desc++) { + if (ifmr.ifm_active & desc->ifmt_word) { + if (options) { + [options appendFormat:@",%s", desc->ifmt_string]; + } else { + options = [NSMutableString stringWithUTF8String:desc->ifmt_string]; + } + } + } + + NSString *media; + if (options) { + media = [NSString stringWithFormat:@"%s <%@>", + type, + options]; + } else { + media = [NSString stringWithUTF8String:type]; + } + + return media; +} + +- (void)linkStatusChange:(NSDictionary *)newValue { + int active = [[newValue objectForKey:@"Active"] intValue]; + + if (active) { + NSString *media = [self getMediaForInterface:"en0"]; + NSString *desc = [NSString stringWithFormat: + NSLocalizedString(@"Interface:\ten0\nMedia:\t%@", "The %@ will be replaced by a description of the Ethernet media such as '100BT/full-duplex'"), + media]; + AppController_linkUp((CFStringRef)desc); + } else + AppController_linkDown((CFStringRef)NSLocalizedString(@"Interface:\ten0", nil)); +} + +/* + The following comments are for the benefit of genstrings: + + NSLocalizedString(@"Private", "Type of IP address") + NSLocalizedString(@"Private", "Type of IP address") + NSLocalizedString(@"Private", "Type of IP address") + NSLocalizedString(@"Loopback", "Type of IP address") + NSLocalizedString(@"Link-local", "Type of IP address") + NSLocalizedString(@"Test", "Type of IP address") + NSLocalizedString(@"6to4 relay", "Type of IP address") + NSLocalizedString(@"Benchmark", "Type of IP address") + NSLocalizedString(@"Reserved", "Type of IP address") + */ +- (NSString *)typeOfIP:(CFStringRef)ipString { + static struct { + in_addr_t network; + in_addr_t netmask; + NSString *type; + } const types[9] = { + // RFC 1918 addresses + { 0x0A000000, 0xFF000000, @"Private" }, // 10.0.0.0/8 + { 0xAC100000, 0xFFF00000, @"Private" }, // 172.16.0.0/12 + { 0xC0A80000, 0xFFFF0000, @"Private" }, // 192.168.0.0/16 + // Other RFC 3330 addresses + { 0x7F000000, 0xFF000000, @"Loopback" }, // 127.0.0.0/8 + { 0xA9FE0000, 0xFFFF0000, @"Link-local" }, // 169.254.0.0/16 + { 0xC0000200, 0xFFFFFF00, @"Test" }, // 192.0.2.0/24 + { 0xC0586200, 0xFFFFFF00, @"6to4 relay" }, // 192.88.99.0/24 + { 0xC6120000, 0xFFFE0000, @"Benchmark" }, // 198.18.0.0/15 + { 0xF0000000, 0xF0000000, @"Reserved" } // 240.0.0.0/4 + }; + struct in_addr addr; + char ip[16]; + CFStringGetCString(ipString, ip, sizeof(ip), kCFStringEncodingASCII); + if (inet_pton(AF_INET, ip, &addr) > 0) { + uint32_t a = ntohl(addr.s_addr); + for (unsigned i=0U; i<9; ++i) { + if ((a & types[i].netmask) == types[i].network) { + return [[NSBundle bundleForClass:[self class]] localizedStringForKey:types[i].type + value:types[i].type + table:nil]; + } + } + } + + return NSLocalizedString(@"Public", "Type of IP address"); +} + +- (void)ipAddressChange:(NSDictionary *)newValue { + if (newValue) { + //Get a key to look up the actual IPv4 info in the dynStore + NSString *ipv4Key = [NSString stringWithFormat:@"State:/Network/Interface/%@/IPv4", + [newValue objectForKey:@"PrimaryInterface"]]; + CFDictionaryRef ipv4Info = SCDynamicStoreCopyValue(dynStore, (CFStringRef)ipv4Key); + if (ipv4Info) { + CFArrayRef addrs = CFDictionaryGetValue(ipv4Info, CFSTR("Addresses")); + if (addrs && CFArrayGetCount(addrs)) { + CFStringRef ip = CFArrayGetValueAtIndex(addrs, 0); + AppController_ipAcquired(ip, (CFStringRef)[self typeOfIP:ip]); + } + CFRelease(ipv4Info); + } + } else + AppController_ipReleased(); +} + +- (void)airportStatusChange:(NSDictionary *)newValue { +// NSLog(CFSTR("AirPort event")); + + CFDataRef newBSSID = NULL; + if (newValue) + newBSSID = (CFDataRef)[newValue objectForKey:@"BSSID"]; + + CFDataRef oldBSSID = NULL; + if (airportStatus) + oldBSSID = CFDictionaryGetValue(airportStatus, CFSTR("BSSID")); + + if (newValue && oldBSSID != newBSSID && !(newBSSID && oldBSSID && CFEqual(oldBSSID, newBSSID))) { + NSNumber *linkStatus = [newValue objectForKey:@"Link Status"]; + NSNumber *powerStatus = [newValue objectForKey:@"Power Status"]; + if (linkStatus || powerStatus) { + int status = 0; + if (linkStatus) { + status = [linkStatus intValue]; + } else if (powerStatus) { + status = [powerStatus intValue]; + status = !status; + } + if (status == AIRPORT_DISCONNECTED) { + CFStringRef networkName = CFDictionaryGetValue(airportStatus, CFSTR("SSID_STR")); + if (!networkName) + networkName = CFDictionaryGetValue(airportStatus, CFSTR("SSID")); + AppController_airportDisconnect(networkName); + } else { + NSString *networkName = [newValue objectForKey:@"SSID_STR"]; + if (!networkName) + networkName = [newValue objectForKey:@"SSID"]; + AppController_airportConnect((CFStringRef)networkName, CFDataGetBytePtr(newBSSID)); + } + } + } + + if (airportStatus) + CFRelease(airportStatus); + + if (newValue) + airportStatus = CFRetain(newValue); + else + airportStatus = NULL; +} + +static void scCallback(SCDynamicStoreRef store, CFArrayRef changedKeys, void *info) { + NetworkNotifier *self = info; + + CFIndex count = CFArrayGetCount(changedKeys); + for (CFIndex i=0; i +#include +#include + +#include "AppController.h" + +extern void NSLog(CFStringRef format, ...); + +static CFRunLoopSourceRef powerNotifierRunLoopSource = NULL; +static HGPowerSource lastPowerSource; +static CFBooleanRef lastChargingState; +static CFIndex lastBatteryTime = -1; + +static bool stringsAreEqual(CFStringRef a, CFStringRef b) +{ + if (!a || !b) return 0; + + return (CFStringCompare(a, b, 0) == kCFCompareEqualTo); +} + +static void powerSourceChanged(void *context) +{ +#pragma unused(context) + CFTypeRef powerBlob = IOPSCopyPowerSourcesInfo(); + CFArrayRef powerSourcesList = IOPSCopyPowerSourcesList(powerBlob); + + CFIndex count = CFArrayGetCount(powerSourcesList); + for (CFIndex i = 0; i < count; ++i) { + CFTypeRef powerSource; + CFDictionaryRef description; + + HGPowerSource hgPowerSource; + CFBooleanRef charging = kCFBooleanFalse; + CFIndex batteryTime = -1; + CFIndex percentageCapacity = -1; + + powerSource = CFArrayGetValueAtIndex(powerSourcesList, i); + description = IOPSGetPowerSourceDescription(powerBlob, powerSource); + + //Don't display anything for power sources that aren't present (i.e. an absent second battery in a 2-battery machine) + if (CFDictionaryGetValue(description, CFSTR(kIOPSIsPresentKey)) == kCFBooleanFalse) + continue; + + //We only know how to handle internal (battery, a/c power) transport types. The other values indicate UPS usage. + if (stringsAreEqual(CFDictionaryGetValue(description, CFSTR(kIOPSTransportTypeKey)), + CFSTR(kIOPSInternalType))) { + CFStringRef currentState = CFDictionaryGetValue(description, CFSTR(kIOPSPowerSourceStateKey)); + if (stringsAreEqual(currentState, CFSTR(kIOPSACPowerValue))) + hgPowerSource = HGACPower; + else if (stringsAreEqual(currentState, CFSTR(kIOPSBatteryPowerValue))) + hgPowerSource = HGBatteryPower; + else + hgPowerSource = HGUnknownPower; + + //Battery power + if (CFDictionaryGetValue(description, CFSTR(kIOPSIsChargingKey)) == kCFBooleanTrue) { + //Charging + charging = kCFBooleanTrue; + + CFNumberRef timeToChargeNum = CFDictionaryGetValue(description, CFSTR(kIOPSTimeToFullChargeKey)); + CFIndex timeToCharge; + + if (CFNumberGetValue(timeToChargeNum, kCFNumberCFIndexType, &timeToCharge)) + batteryTime = timeToCharge; + } else { + //Not charging + charging = kCFBooleanFalse; + + CFNumberRef timeToEmptyNum = CFDictionaryGetValue(description, CFSTR(kIOPSTimeToEmptyKey)); + CFIndex timeToEmpty; + + if (CFNumberGetValue(timeToEmptyNum, kCFNumberCFIndexType, &timeToEmpty)) + batteryTime = timeToEmpty; + } + + /* Capacity */ + CFNumberRef currentCapacityNum = CFDictionaryGetValue(description, CFSTR(kIOPSCurrentCapacityKey)); + CFNumberRef maxCapacityNum = CFDictionaryGetValue(description, CFSTR(kIOPSMaxCapacityKey)); + + CFIndex currentCapacity, maxCapacity; + + if (CFNumberGetValue(currentCapacityNum, kCFNumberCFIndexType, ¤tCapacity) && + CFNumberGetValue(maxCapacityNum, kCFNumberCFIndexType, &maxCapacity)) + percentageCapacity = roundf((currentCapacity / (float)maxCapacity) * 100.0f); + + } else { + //UPS power + hgPowerSource = HGUPSPower; + } + + //Avoid sending notifications on the same power source multiple times, unless the charging state or presence/absence of a time estimate has changed. + if (lastPowerSource != hgPowerSource || lastChargingState != charging || (lastBatteryTime == -1) != (batteryTime == -1)) { + lastPowerSource = hgPowerSource; + lastChargingState = charging; + lastBatteryTime = batteryTime; + AppController_powerSwitched(hgPowerSource, charging, batteryTime, percentageCapacity); + } + } + + CFRelease(powerSourcesList); + CFRelease(powerBlob); +} + +void PowerNotifier_init(void) +{ + powerNotifierRunLoopSource = IOPSNotificationCreateRunLoopSource(powerSourceChanged, + NULL); + if (powerNotifierRunLoopSource) + CFRunLoopAddSource(CFRunLoopGetCurrent(), powerNotifierRunLoopSource, kCFRunLoopDefaultMode); + + lastPowerSource = HGUnknownPower; +} + +void PowerNotifier_dealloc(void) +{ + CFRunLoopRemoveSource(CFRunLoopGetCurrent(), powerNotifierRunLoopSource, kCFRunLoopDefaultMode); + CFRelease(powerNotifierRunLoopSource); +} diff --git a/PowerNotifier.h b/PowerNotifier.h new file mode 100644 index 0000000..a8ff610 --- /dev/null +++ b/PowerNotifier.h @@ -0,0 +1,22 @@ +/* + * PowerNotifier.h + * HardwareGrowler + * + * Created by Evan Schoenberg on 3/27/06. + * + */ + +#ifndef POWER_NOTIFIER_H +#define POWER_NOTIFIER_H + +typedef enum { + HGUnknownPower = -1, + HGACPower = 0, + HGBatteryPower, + HGUPSPower +} HGPowerSource; + +void PowerNotifier_init(void); +void PowerNotifier_dealloc(void); + +#endif diff --git a/SyncNotifier.c b/SyncNotifier.c new file mode 100644 index 0000000..bf48aa5 --- /dev/null +++ b/SyncNotifier.c @@ -0,0 +1,54 @@ +// +// SyncNotifier.c +// HardwareGrowler +// +// Created by Ingmar Stein on 03.09.05. +// Copyright 2005 The Growl Project. All rights reserved. +// + +#include "SyncNotifier.h" +#include "AppController.h" + +extern void NSLog(CFStringRef format, ...); + +static void syncStarted(CFNotificationCenterRef center, + void *observer, + CFStringRef name, + const void *object, + CFDictionaryRef userInfo) { +#pragma unused(center,observer,name,object,userInfo) + AppController_syncStarted(); +} + +static void syncFinished(CFNotificationCenterRef center, + void *observer, + CFStringRef name, + const void *object, + CFDictionaryRef userInfo) { +#pragma unused(center,observer,name,object) + CFStringRef status = CFDictionaryGetValue(userInfo, CFSTR("ISyncPlanStatus")); + if (status && CFStringCompare(status, CFSTR("Finished"), 0) == kCFCompareEqualTo) + AppController_syncFinished(); + // else if (CFStringCompare(status, CFSTR("Cancelled"), 0) == kCFCompareEqualTo) +} + +void SyncNotifier_init(void) { + CFNotificationCenterRef center = CFNotificationCenterGetDistributedCenter(); + CFNotificationCenterAddObserver(/*center*/ center, + /*observer*/ "SyncNotifier", + /*callBack*/ syncStarted, + /*name*/ CFSTR("com.apple.syncservices.ISyncPlanChangedNotification"), + /*object*/ CFSTR("ISyncPlanCreated"), + /*suspensionBehavior*/ CFNotificationSuspensionBehaviorCoalesce); + CFNotificationCenterAddObserver(/*center*/ center, + /*observer*/ "SyncNotifier", + /*callBack*/ syncFinished, + /*name*/ CFSTR("com.apple.syncservices.ISyncPlanChangedNotification"), + /*object*/ CFSTR("ISyncPlanEnded"), + /*suspensionBehavior*/ CFNotificationSuspensionBehaviorCoalesce); +} + +void SyncNotifier_dealloc(void) { + CFNotificationCenterRemoveEveryObserver(CFNotificationCenterGetDistributedCenter(), + "SyncNotifier"); +} diff --git a/SyncNotifier.h b/SyncNotifier.h new file mode 100644 index 0000000..a1f9528 --- /dev/null +++ b/SyncNotifier.h @@ -0,0 +1,10 @@ +// +// SyncNotifier.h +// HardwareGrowler +// +// Created by Ingmar Stein on 03.09.05. +// Copyright 2005 The Growl Project. All rights reserved. +// + +void SyncNotifier_init(void); +void SyncNotifier_dealloc(void); diff --git a/USBNotifier.c b/USBNotifier.c new file mode 100644 index 0000000..6835b86 --- /dev/null +++ b/USBNotifier.c @@ -0,0 +1,163 @@ +#include "USBNotifier.h" +#include "AppController.h" +#include +#include +#include +#include + +extern void NSLog(CFStringRef format, ...); + +static IONotificationPortRef ioKitNotificationPort; +static CFRunLoopSourceRef notificationRunLoopSource; +static Boolean notificationsArePrimed = false; + +#pragma mark C Callbacks + +static void usbDeviceAdded(void *refCon, io_iterator_t iterator) { +#pragma unused(refCon) +// NSLog(@"USB Device Added Notification."); + io_object_t thisObject; + while ((thisObject = IOIteratorNext(iterator))) { + Boolean keyExistsAndHasValidFormat; + if (notificationsArePrimed || CFPreferencesGetAppBooleanValue(CFSTR("ShowExisting"), CFSTR("com.growl.hardwaregrowler"), &keyExistsAndHasValidFormat)) { + kern_return_t nameResult; + io_name_t deviceNameChars; + + // This works with USB devices... + // but apparently not firewire + nameResult = IORegistryEntryGetName(thisObject, deviceNameChars); + if (nameResult != KERN_SUCCESS) { + NSLog(CFSTR("%s: Could not get name for USB object %p: IORegistryEntryGetName returned 0x%x"), __PRETTY_FUNCTION__, thisObject, nameResult); + continue; + } + + CFStringRef deviceName = CFStringCreateWithCString(kCFAllocatorDefault, + deviceNameChars, + kCFStringEncodingASCII); + if (deviceName) { + if ((CFStringCompare(deviceName, CFSTR("OHCI Root Hub Simulation"), 0) == kCFCompareEqualTo) || + (CFStringCompare(deviceName, CFSTR("UHCI Root Hub Simulation"), 0) == kCFCompareEqualTo)) { + CFRelease(deviceName); + deviceName = CFCopyLocalizedString(CFSTR("USB Bus"), ""); + } else if (CFStringCompare(deviceName, CFSTR("EHCI Root Hub Simulation"), 0) == kCFCompareEqualTo) { + CFRelease(deviceName); + deviceName = CFCopyLocalizedString(CFSTR("USB 2.0 Bus"), ""); + } + + // NSLog(@"USB Device Attached: %@" , deviceName); + AppController_usbDidConnect(deviceName); + CFRelease(deviceName); + } + } + + IOObjectRelease(thisObject); + } +} + +static void usbDeviceRemoved(void *refCon, io_iterator_t iterator) { +#pragma unused(refCon) +// NSLog(@"USB Device Removed Notification."); + io_object_t thisObject; + while ((thisObject = IOIteratorNext(iterator))) { + kern_return_t nameResult; + io_name_t deviceNameChars; + + // This works with USB devices... + // but apparently not firewire + nameResult = IORegistryEntryGetName(thisObject, deviceNameChars); + if (nameResult != KERN_SUCCESS) { + NSLog(CFSTR("%s: Could not get name for USB object %p: IORegistryEntryGetName returned 0x%x"), __PRETTY_FUNCTION__, thisObject, nameResult); + continue; + } + + CFStringRef deviceName = CFStringCreateWithCString(kCFAllocatorDefault, + deviceNameChars, + kCFStringEncodingASCII); + if (deviceName) { + if (CFStringCompare(deviceName, CFSTR("OHCI Root Hub Simulation"), 0) == kCFCompareEqualTo) { + CFRelease(deviceName); + deviceName = CFCopyLocalizedString(CFSTR("USB Bus"), ""); + } else if (CFStringCompare(deviceName, CFSTR("EHCI Root Hub Simulation"), 0) == kCFCompareEqualTo) { + CFRelease(deviceName); + deviceName = CFCopyLocalizedString(CFSTR("USB 2.0 Bus"), ""); + } + + // NSLog(@"USB Device Detached: %@" , deviceName); + AppController_usbDidDisconnect(deviceName); + CFRelease(deviceName); + } + + IOObjectRelease(thisObject); + } +} + +#pragma mark - + +static void registerForUSBNotifications(void) { + //http://developer.apple.com/documentation/DeviceDrivers/Conceptual/AccessingHardware/AH_Finding_Devices/chapter_4_section_2.html#//apple_ref/doc/uid/TP30000379/BABEACCJ + kern_return_t matchingResult; + kern_return_t removeNoteResult; + io_iterator_t addedIterator; + io_iterator_t removedIterator; + +// NSLog(@"registerForUSBNotifications"); + + // Setup a matching Dictionary. + CFDictionaryRef myMatchDictionary; + myMatchDictionary = IOServiceMatching(kIOUSBDeviceClassName); + + // Register our notification + matchingResult = IOServiceAddMatchingNotification(ioKitNotificationPort, + kIOPublishNotification, + myMatchDictionary, + usbDeviceAdded, + NULL, + &addedIterator); + + if (matchingResult) + NSLog(CFSTR("matching notification registration failed: %d"), matchingResult); + + // Prime the Notifications (And Deal with the existing devices)... + usbDeviceAdded(NULL, addedIterator); + + // Register for removal notifications. + // It seems we have to make a new dictionary... reusing the old one didn't work. + + myMatchDictionary = IOServiceMatching(kIOUSBDeviceClassName); + removeNoteResult = IOServiceAddMatchingNotification(ioKitNotificationPort, + kIOTerminatedNotification, + myMatchDictionary, + usbDeviceRemoved, + NULL, + &removedIterator); + + // Matching notification must be "primed" by iterating over the + // iterator returned from IOServiceAddMatchingNotification(), so + // we call our device removed method here... + // + if (kIOReturnSuccess != removeNoteResult) + NSLog(CFSTR("Couldn't add device removal notification")); + else + usbDeviceRemoved(NULL, removedIterator); + + notificationsArePrimed = true; +} + +void USBNotifier_init(void) { + notificationsArePrimed = false; +//#warning kIOMasterPortDefault is only available on 10.2 and above... + ioKitNotificationPort = IONotificationPortCreate(kIOMasterPortDefault); + notificationRunLoopSource = IONotificationPortGetRunLoopSource(ioKitNotificationPort); + + CFRunLoopAddSource(CFRunLoopGetCurrent(), + notificationRunLoopSource, + kCFRunLoopDefaultMode); + registerForUSBNotifications(); +} + +void USBNotifier_dealloc(void) { + if (ioKitNotificationPort) { + CFRunLoopRemoveSource(CFRunLoopGetCurrent(), notificationRunLoopSource, kCFRunLoopDefaultMode); + IONotificationPortDestroy(ioKitNotificationPort); + } +} diff --git a/USBNotifier.h b/USBNotifier.h new file mode 100644 index 0000000..d5fb80b --- /dev/null +++ b/USBNotifier.h @@ -0,0 +1,4 @@ +/* USBNotifier */ + +void USBNotifier_init(void); +void USBNotifier_dealloc(void); diff --git a/VolumeNotifier.h b/VolumeNotifier.h new file mode 100644 index 0000000..d1ef3c5 --- /dev/null +++ b/VolumeNotifier.h @@ -0,0 +1,35 @@ +// +// VolumeNotifier.h +// HardwareGrowler +// +// Created by Diggory Laycock on 10/02/2005. +// Copyright 2005 The Growl Project. All rights reserved. +// + +void VolumeNotifier_init(void); +void VolumeNotifier_dealloc(void); + +#ifdef __OBJC__ + +#import + +@interface VolumeInfo : NSObject { + NSData *iconData; + NSString *name; + NSString *path; +} + ++ (VolumeInfo *) volumeInfoForMountWithPath:(NSString *)aPath; ++ (VolumeInfo *) volumeInfoForUnmountWithPath:(NSString *)aPath; + +- (id) initForMountWithPath:(NSString *)aPath; +- (id) initForUnmountWithPath:(NSString *)aPath; +- (id) initWithPath:(NSString *)aPath; + +- (NSData *) iconData; +- (NSString *) name; +- (NSString *) path; + +@end + +#endif diff --git a/VolumeNotifier.m b/VolumeNotifier.m new file mode 100644 index 0000000..655eb52 --- /dev/null +++ b/VolumeNotifier.m @@ -0,0 +1,256 @@ +// +// VolumeNotifier.c +// HardwareGrowler +// +// Created by Diggory Laycock on 10/02/2005. +// Copyright 2005 The Growl Project. All rights reserved. +// + +#import "VolumeNotifier.h" +#import "AppController.h" + + +// wait 10 minutes for a corresponding did unmount notification +#define VolumeNotifierUnmountWaitSeconds 600.0 +#define VolumeEjectCacheInfoIndex 0 +#define VolumeEjectCacheTimerIndex 1 + +static NSMutableDictionary *ejectCache = nil; + +#pragma mark Icons + +static NSImage *ejectIconImage(void) +{ + //Named with an underscore to prevent name conflict with the function. Be aware of which one you use here. + static NSImage *_ejectIconImage = nil; + + if (!_ejectIconImage) { + _ejectIconImage = [[[NSWorkspace sharedWorkspace] iconForFileType:NSFileTypeForHFSTypeCode(kEjectMediaIcon)] retain]; + } + + return _ejectIconImage; +} + +static NSData *mountIconData(void) +{ + //Named with an underscore to prevent name conflict with the function. Be aware of which one you use here. + static NSData *_mountIconData = nil; + + if (!_mountIconData) { + _mountIconData = [[[[NSWorkspace sharedWorkspace] iconForFileType:NSFileTypeForHFSTypeCode(kGenericRemovableMediaIcon)] TIFFRepresentation] retain]; + } + + return _mountIconData; +} + +@implementation VolumeInfo + ++ (VolumeInfo *) volumeInfoForMountWithPath:(NSString *)aPath { + return [[[VolumeInfo alloc] initForMountWithPath:aPath] autorelease]; +} + ++ (VolumeInfo *) volumeInfoForUnmountWithPath:(NSString *)aPath { + return [[[VolumeInfo alloc] initForUnmountWithPath:aPath] autorelease]; +} + +- (id) initForMountWithPath:(NSString *)aPath { + if ((self = [self initWithPath:aPath])) { + if (path) { +// iconData = copyIconDataForPath(path); + } else { + iconData = [mountIconData() retain]; + } + } + + return self; +} + +- (id) initForUnmountWithPath:(NSString *)aPath { + if ((self = [self initWithPath:aPath])) { + if (path) { + //Get the icon for the volume. + //NSImage *icon = [[[NSImage alloc] initWithData:[copyIconDataForPath(path) autorelease]] autorelease]; + //NSSize iconSize = [icon size]; + //Also get the standard Eject icon. + NSImage *ejectIcon = ejectIconImage(); + [ejectIcon setScalesWhenResized:NO]; //Use the high-res rep instead. + //NSSize ejectIconSize = [ejectIcon size]; + + //Badge the volume icon with the Eject icon. This is what we'll pass off te Growl. + //The badge's width and height are 2/3 of the overall icon's width and height. If they were 1/2, it would look small (so I found in testing —boredzo). This looks pretty good. + //[icon lockFocus]; + + //[ejectIcon drawInRect:NSMakeRect( /*origin.x*/ iconSize.width * (1.0f / 3.0f), /*origin.y*/ 0.0f, /*width*/ iconSize.width * (2.0f / 3.0f), /*height*/ iconSize.height * (2.0f / 3.0f) ) + // fromRect:(NSRect){ NSZeroPoint, ejectIconSize } + // operation:NSCompositeSourceOver + // fraction:1.0f]; + + //For some reason, passing [icon TIFFRepresentation] only passes the unbadged volume icon to Growl, even though writing the same TIFF data out to a file and opening it in Preview does show the badge. If anybody can figure that out, you're welcome to do so. Until then: + //We get a NSBIR for the current focused view (the image), and make PNG data from it. (There is no reason why this could not be TIFF if we wanted it to be. I just generally prefer PNG. —boredzo) + //NSBitmapImageRep *imageRep = [[[NSBitmapImageRep alloc] initWithFocusedViewRect:(NSRect){ NSZeroPoint, iconSize }] autorelease]; +// iconData = [[imageRep representationUsingType:NSPNGFileType properties:nil] retain]; + +// [icon unlockFocus]; + } else { + iconData = [[ejectIconImage() TIFFRepresentation] retain]; + } + } + + return self; +} + +- (id) initWithPath:(NSString *)aPath { + if ((self = [super init])) { + if (aPath) { + path = [aPath retain]; + name = [[[NSFileManager defaultManager] displayNameAtPath:path] retain]; + } + } + + return self; +} + +- (void) dealloc { + [path release]; + path = nil; + + [name release]; + name = nil; + + [iconData release]; + iconData = nil; + + [super dealloc]; +} + +- (NSString *) description { + NSMutableDictionary *desc = [NSMutableDictionary dictionary]; + + if (name) + [desc setObject:name forKey:@"name"]; + if (path) + [desc setObject:path forKey:@"path"]; + if (iconData) + [desc setObject:@"" forKey:@"iconData"]; + + return [desc description]; +} + +- (NSData *) iconData { + return iconData; +} + +- (NSString *) name { + return name; +} + +- (NSString *) path { + return path; +} + +@end + +@interface VolumeNotifier : NSObject { +} + ++ (void) staleEjectItemTimerFired:(NSTimer *)theTimer; ++ (void) volumeDidMount:(NSNotification *)aNotification; ++ (void) volumeWillUnmount:(NSNotification *)aNotification; ++ (void) volumeDidUnmount:(NSNotification *)aNotification; + +@end + +@implementation VolumeNotifier + ++ (void) staleEjectItemTimerFired:(NSTimer *)theTimer { + VolumeInfo *info = [theTimer userInfo]; + + [ejectCache removeObjectForKey:[info path]]; +} + ++ (void) volumeDidMount:(NSNotification *)aNotification { + AppController_volumeDidMount([VolumeInfo volumeInfoForMountWithPath:[[aNotification userInfo] objectForKey:@"NSDevicePath"]]); +} + ++ (void) volumeWillUnmount:(NSNotification *)aNotification { + NSString *path = [[aNotification userInfo] objectForKey:@"NSDevicePath"]; + + if (path) { + VolumeInfo *info = [VolumeInfo volumeInfoForUnmountWithPath:path]; + NSTimer *timer = [NSTimer scheduledTimerWithTimeInterval:VolumeNotifierUnmountWaitSeconds + target:[VolumeNotifier class] + selector:@selector(staleEjectItemTimerFired:) + userInfo:info + repeats:NO]; + + // need to invalidate the timer for a previous item if it exists + NSArray *cacheItem = [ejectCache objectForKey:path]; + if (cacheItem) + [[cacheItem objectAtIndex:VolumeEjectCacheTimerIndex] invalidate]; + + [ejectCache setObject:[NSArray arrayWithObjects:info, timer, nil] forKey:path]; + } +} + ++ (void) volumeDidUnmount:(NSNotification *)aNotification { + VolumeInfo *info = nil; + NSString *path = [[aNotification userInfo] objectForKey:@"NSDevicePath"]; + NSArray *cacheItem = path ? [ejectCache objectForKey:path] : nil; + + if (cacheItem) + info = [cacheItem objectAtIndex:VolumeEjectCacheInfoIndex]; + else + info = [VolumeInfo volumeInfoForUnmountWithPath:path]; + + AppController_volumeDidUnmount(info); + + if (cacheItem) { + [[cacheItem objectAtIndex:VolumeEjectCacheTimerIndex] invalidate]; + // we need to remove the item from the cache AFTER calling volumeDidUnmount so that "info" stays + // retained long enough to be useful. After this next call, "info" is no longer valid. + [ejectCache removeObjectForKey:path]; + info = nil; + } +} + +@end + +void VolumeNotifier_init(void) { + if (!ejectCache) + ejectCache = [[NSMutableDictionary alloc] init]; + + NSWorkspace *workspace = [NSWorkspace sharedWorkspace]; + NSNotificationCenter *center = [workspace notificationCenter]; + + [center addObserver:[VolumeNotifier class] selector:@selector(volumeDidMount:) name:NSWorkspaceDidMountNotification object:nil]; + //Note that we must use both WILL and DID unmount, so we can only get the volume's icon before the volume has finished unmounting. + //The icon and data is stored during WILL unmount, and then displayed during DID unmount. + [center addObserver:[VolumeNotifier class] selector:@selector(volumeDidUnmount:) name:NSWorkspaceDidUnmountNotification object:nil]; + [center addObserver:[VolumeNotifier class] selector:@selector(volumeWillUnmount:) name:NSWorkspaceWillUnmountNotification object:nil]; + + Boolean keyExistsAndHasValidFormat; + if (CFPreferencesGetAppBooleanValue(CFSTR("ShowExisting"), CFSTR("com.growl.hardwaregrowler"), &keyExistsAndHasValidFormat)) { + NSArray *paths = [workspace mountedLocalVolumePaths]; + unsigned int i; + + for (i = 0; i < [paths count]; ++i) + AppController_volumeDidMount([VolumeInfo volumeInfoForMountWithPath:[paths objectAtIndex:i]]); + } +} + +void VolumeNotifier_dealloc(void) { + NSNotificationCenter *center = [[NSWorkspace sharedWorkspace] notificationCenter]; + + [center removeObserver:[VolumeNotifier class] name:NSWorkspaceWillUnmountNotification object:nil]; + [center removeObserver:[VolumeNotifier class] name:NSWorkspaceDidUnmountNotification object:nil]; + [center removeObserver:[VolumeNotifier class] name:NSWorkspaceDidMountNotification object:nil]; + + // loop through the eject cache and invalidate all the timers + NSEnumerator *cacheItemEnum = [ejectCache objectEnumerator]; + for (NSArray *cacheItem = [cacheItemEnum nextObject]; cacheItem != nil; cacheItem = [cacheItemEnum nextObject]) + [[cacheItem objectAtIndex:VolumeEjectCacheTimerIndex] invalidate]; + + [ejectCache release]; + ejectCache = nil; +} + diff --git a/eject.icns b/eject.icns new file mode 100644 index 0000000..eed75c1 Binary files /dev/null and b/eject.icns differ diff --git a/hwGrowlerIcon.icns b/hwGrowlerIcon.icns new file mode 100644 index 0000000..794f497 Binary files /dev/null and b/hwGrowlerIcon.icns differ diff --git a/hwGrowlerIcon.png b/hwGrowlerIcon.png new file mode 100644 index 0000000..b8166e9 Binary files /dev/null and b/hwGrowlerIcon.png differ diff --git a/main.c b/main.c new file mode 100644 index 0000000..21c6a84 --- /dev/null +++ b/main.c @@ -0,0 +1,13 @@ +// +// main.c +// HarwdareGrowler +// +// Created by Diggory Laycock on 09/02/2005. +// Copyright The Growl Project 2005. All rights reserved. +// + +extern int NSApplicationMain(int argc, const char *argv[]); + +int main(int argc, const char *argv[]) { + return NSApplicationMain(argc, argv); +} diff --git a/readme.txt b/readme.txt new file mode 100644 index 0000000..4a5f696 --- /dev/null +++ b/readme.txt @@ -0,0 +1,7 @@ +HardwareGrowler +*************** + +Initially by Diggory Laycock (http://www.monkeyfood.com) +10th Feb 2005 + +Modified for Notification Center by cirrus \ No newline at end of file diff --git a/version.plist b/version.plist new file mode 100644 index 0000000..94985e7 --- /dev/null +++ b/version.plist @@ -0,0 +1,16 @@ + + + + + BuildVersion + 1.1.6 + CFBundleVersion + 1.1.6 + ProductBuildVersion + 7K571 + ProjectName + NibPBTemplates + SourceVersion + 1200000 + +