aboutsummaryrefslogtreecommitdiffstats
path: root/lib
diff options
context:
space:
mode:
authorDominik Pich2013-03-17 13:33:20 +0100
committerDominik Pich2013-03-17 13:33:20 +0100
commitd4ffd217288cfc0d2798b3df67ab20bc867a7f7e (patch)
treeab40c6fb784fbe578713381ea5cf1159934a9c34 /lib
parent77d2a9f99a289d247c5a8561879489c8f8162055 (diff)
downloadDDHidLib-d4ffd217288cfc0d2798b3df67ab20bc867a7f7e.tar.bz2
DDHIDLib 1.1.1
Diffstat (limited to 'lib')
-rw-r--r--lib/DDHidAppleMikey.h66
-rw-r--r--lib/DDHidAppleMikey.m164
-rw-r--r--lib/DDHidAppleRemote.h84
-rw-r--r--lib/DDHidAppleRemote.m236
-rw-r--r--lib/DDHidDevice.h131
-rw-r--r--lib/DDHidDevice.m585
-rw-r--r--lib/DDHidElement.h65
-rw-r--r--lib/DDHidElement.m185
-rw-r--r--lib/DDHidEvent.h45
-rw-r--r--lib/DDHidEvent.m81
-rw-r--r--lib/DDHidJoystick.h132
-rw-r--r--lib/DDHidJoystick.m654
-rw-r--r--lib/DDHidKeyboard.h68
-rw-r--r--lib/DDHidKeyboard.m173
-rw-r--r--lib/DDHidKeyboardBarcodeScanner.h74
-rw-r--r--lib/DDHidKeyboardBarcodeScanner.m222
-rw-r--r--lib/DDHidLib.h36
-rw-r--r--lib/DDHidMouse.h78
-rw-r--r--lib/DDHidMouse.m279
-rw-r--r--lib/DDHidQueue.h70
-rw-r--r--lib/DDHidQueue.m172
-rw-r--r--lib/DDHidStandardUsages.plist2327
-rw-r--r--lib/DDHidUsage.h52
-rw-r--r--lib/DDHidUsage.m84
-rw-r--r--lib/DDHidUsageTables.h40
-rw-r--r--lib/DDHidUsageTables.m85
-rw-r--r--lib/NSDictionary+DDHidExtras.h46
-rw-r--r--lib/NSDictionary+DDHidExtras.m81
28 files changed, 6315 insertions, 0 deletions
diff --git a/lib/DDHidAppleMikey.h b/lib/DDHidAppleMikey.h
new file mode 100644
index 0000000..c6bea0d
--- /dev/null
+++ b/lib/DDHidAppleMikey.h
@@ -0,0 +1,66 @@
+/*
+ * Copyright (c) 2007 Dave Dribin
+ *
+ * Permission is hereby granted, free of charge, to any person
+ * obtaining a copy of this software and associated documentation
+ * files (the "Software"), to deal in the Software without
+ * restriction, including without limitation the rights to use, copy,
+ * modify, merge, publish, distribute, sublicense, and/or sell copies
+ * of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be
+ * included in all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+ * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
+ * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
+ * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
+ * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+ * SOFTWARE.
+ */
+
+#import <Cocoa/Cocoa.h>
+#import "DDHidDevice.h"
+
+@class DDHidElement;
+@class DDHidQueue;
+
+@interface DDHidAppleMikey : DDHidDevice
+{
+ NSMutableArray * mPressElements;
+
+ id mDelegate;
+}
+
++ (NSArray *) allMikeys;
+
+- (id) initWithDevice: (io_object_t) device error: (NSError **) error_;
+
+#pragma mark -
+#pragma mark Elements
+
+- (NSArray *) pressElements;
+
+- (unsigned) numberOfKeys;
+
+- (void) addElementsToQueue: (DDHidQueue *) queue;
+
+#pragma mark -
+#pragma mark Asynchronous Notification
+
+- (void) setDelegate: (id) delegate;
+
+- (void) addElementsToDefaultQueue;
+
+@end
+
+@interface NSObject (DDHidAppleMikeyDelegate)
+
+- (void) ddhidAppleMikey: (DDHidAppleMikey *) mikey
+ press: (unsigned) usageId
+ upOrDown:(BOOL)upOrDown;
+
+@end
diff --git a/lib/DDHidAppleMikey.m b/lib/DDHidAppleMikey.m
new file mode 100644
index 0000000..b0da05f
--- /dev/null
+++ b/lib/DDHidAppleMikey.m
@@ -0,0 +1,164 @@
+/*
+ * Copyright (c) 2007 Dave Dribin
+ *
+ * Permission is hereby granted, free of charge, to any person
+ * obtaining a copy of this software and associated documentation
+ * files (the "Software"), to deal in the Software without
+ * restriction, including without limitation the rights to use, copy,
+ * modify, merge, publish, distribute, sublicense, and/or sell copies
+ * of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be
+ * included in all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+ * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
+ * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
+ * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
+ * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+ * SOFTWARE.
+ */
+
+#import "DDHidAppleMikey.h"
+#import "DDHidElement.h"
+#import "DDHidUsage.h"
+#import "DDHidQueue.h"
+#import "DDHidEvent.h"
+#include <IOKit/hid/IOHIDUsageTables.h>
+
+@interface DDHidAppleMikey (DDHidAppleMikeyDelegate)
+
+- (void) ddhidAppleMikey: (DDHidAppleMikey *) mikey
+ press: (unsigned) usageId
+ upOrDown:(BOOL)upOrDown;
+
+@end
+
+@interface DDHidAppleMikey (Private)
+
+- (void) initPressElements: (NSArray *) elements;
+- (void) ddhidQueueHasEvents: (DDHidQueue *) hidQueue;
+
+@end
+
+@implementation DDHidAppleMikey
+
++ (NSArray *) allMikeys;
+{
+ //add mikey
+ CFMutableDictionaryRef hidMatchDictionary =
+ IOServiceMatching(kIOHIDDeviceKey);
+ id a2 = [self allDevicesMatchingCFDictionary: hidMatchDictionary
+ withClass: self
+ skipZeroLocations: NO];
+ a2 = [a2 filteredArrayUsingPredicate:[NSPredicate predicateWithFormat:@"productName == \"Apple Mikey HID Driver\""]];
+
+ return a2;
+}
+
+- (id) initWithDevice: (io_object_t) device error: (NSError **) error_;
+{
+ self = [super initWithDevice: device error: error_];
+ if (self == nil)
+ return nil;
+
+ mPressElements = [[NSMutableArray alloc] init];
+ [self initPressElements: [self elements]];
+
+ return self;
+}
+
+//===========================================================
+// dealloc
+//===========================================================
+- (void) dealloc
+{
+ [mPressElements release];
+
+ mPressElements = nil;
+ [super dealloc];
+}
+
+#pragma mark -
+#pragma mark Elements
+
+- (NSArray *) pressElements;
+{
+ return mPressElements;
+}
+
+- (unsigned) numberOfKeys;
+{
+ return [mPressElements count];
+}
+
+- (void) addElementsToQueue: (DDHidQueue *) queue;
+{
+ [queue addElements: mPressElements];
+}
+
+#pragma mark -
+#pragma mark Asynchronous Notification
+
+- (void) setDelegate: (id) delegate;
+{
+ mDelegate = delegate;
+}
+
+- (void) addElementsToDefaultQueue;
+{
+ [self addElementsToQueue: mDefaultQueue];
+}
+
+@end
+
+@implementation DDHidAppleMikey (DDHidAppleMikeyDelegate)
+
+- (void) ddhidAppleMikey:(DDHidAppleMikey *)mikey press:(unsigned int)usageId upOrDown:(BOOL)upOrDown
+{
+ if ([mDelegate respondsToSelector: _cmd])
+ [mDelegate ddhidAppleMikey: mikey press: usageId upOrDown:(BOOL)upOrDown];
+}
+
+@end
+
+@implementation DDHidAppleMikey (Private)
+
+- (void) initPressElements: (NSArray *) elements;
+{
+ NSEnumerator * e = [elements objectEnumerator];
+ DDHidElement * element;
+ while (element = [e nextObject])
+ {
+// unsigned usagePage = [[element usage] usagePage];
+// unsigned usageId = [[element usage] usageId];
+// if (usagePage == kHIDPage_KeyboardOrKeypad)
+// {
+// if ((usageId >= 0x04) && (usageId <= 0xA4) ||
+// (usageId >= 0xE0) && (usageId <= 0xE7))
+ {
+ [mPressElements addObject: element];
+ }
+// }
+ NSArray * subElements = [element elements];
+ if (subElements != nil)
+ [self initPressElements: subElements];
+ }
+}
+
+- (void) ddhidQueueHasEvents: (DDHidQueue *) hidQueue;
+{
+ DDHidEvent * event;
+ while (event = [hidQueue nextEvent])
+ {
+ DDHidElement * element = [self elementForCookie: [event elementCookie]];
+ unsigned usageId = [[element usage] usageId];
+ SInt32 value = [event value];
+ [self ddhidAppleMikey:self press:usageId upOrDown:value==1];
+ }
+}
+
+@end
diff --git a/lib/DDHidAppleRemote.h b/lib/DDHidAppleRemote.h
new file mode 100644
index 0000000..f6bc8ef
--- /dev/null
+++ b/lib/DDHidAppleRemote.h
@@ -0,0 +1,84 @@
+/*
+ * Copyright (c) 2007 Dave Dribin
+ *
+ * Permission is hereby granted, free of charge, to any person
+ * obtaining a copy of this software and associated documentation
+ * files (the "Software"), to deal in the Software without
+ * restriction, including without limitation the rights to use, copy,
+ * modify, merge, publish, distribute, sublicense, and/or sell copies
+ * of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be
+ * included in all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+ * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
+ * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
+ * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
+ * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+ * SOFTWARE.
+ */
+
+#import <Cocoa/Cocoa.h>
+
+#import "DDHidDevice.h"
+
+@class DDHidElement;
+
+enum DDHidAppleRemoteEventIdentifier
+{
+ kDDHidRemoteButtonVolume_Plus=0,
+ kDDHidRemoteButtonVolume_Minus,
+ kDDHidRemoteButtonMenu,
+ kDDHidRemoteButtonPlay,
+ kDDHidRemoteButtonRight,
+ kDDHidRemoteButtonLeft,
+ kDDHidRemoteButtonRight_Hold,
+ kDDHidRemoteButtonLeft_Hold,
+ kDDHidRemoteButtonMenu_Hold,
+ kDDHidRemoteButtonPlay_Sleep,
+ kDDHidRemoteControl_Switched,
+ kDDHidRemoteControl_Paired,
+};
+typedef enum DDHidAppleRemoteEventIdentifier DDHidAppleRemoteEventIdentifier;
+
+@interface DDHidAppleRemote : DDHidDevice
+{
+ NSMutableDictionary * mCookieToButtonMapping;
+ NSArray * mButtonElements;
+ DDHidElement * mIdElement;
+ int mRemoteId;
+
+ id mDelegate;
+}
+
++ (NSArray *) allRemotes;
+
++ (DDHidAppleRemote *) firstRemote;
+
+- (id) initWithDevice: (io_object_t) device error: (NSError **) error_;
+
+#pragma mark -
+#pragma mark Asynchronous Notification
+
+- (void) setDelegate: (id) delegate;
+
+- (void) addElementsToDefaultQueue;
+
+#pragma mark -
+#pragma mark Properties
+
+- (int) remoteId;
+- (void) setRemoteId: (int) theRemoteId;
+
+@end
+
+@interface NSObject (DDHidAppleRemoteDelegate)
+
+- (void) ddhidAppleRemoteButton: (DDHidAppleRemoteEventIdentifier) buttonIdentifier
+ pressedDown: (BOOL) pressedDown;
+
+@end
diff --git a/lib/DDHidAppleRemote.m b/lib/DDHidAppleRemote.m
new file mode 100644
index 0000000..e8b31b2
--- /dev/null
+++ b/lib/DDHidAppleRemote.m
@@ -0,0 +1,236 @@
+/*
+ * Copyright (c) 2007 Dave Dribin
+ *
+ * Permission is hereby granted, free of charge, to any person
+ * obtaining a copy of this software and associated documentation
+ * files (the "Software"), to deal in the Software without
+ * restriction, including without limitation the rights to use, copy,
+ * modify, merge, publish, distribute, sublicense, and/or sell copies
+ * of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be
+ * included in all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+ * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
+ * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
+ * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
+ * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+ * SOFTWARE.
+ */
+
+// Based on Martin Kahr's Apple Remote wrapper
+// http://www.martinkahr.com/source-code/
+
+#import "DDHidAppleRemote.h"
+#import "DDHidElement.h"
+#import "DDHidUsage.h"
+#import "DDHidQueue.h"
+#import "DDHidEvent.h"
+#import "NSDictionary+DDHidExtras.h"
+
+@interface DDHidAppleRemote (Private)
+
+- (void) initRemoteElements: (NSArray *) elements;
+
+- (void) ddhidQueueHasEvents: (DDHidQueue *) hidQueue;
+
+- (void) handleEventWithCookieString: (NSString*) cookieString
+ sumOfValues: (SInt32) sumOfValues;
+
+@end
+
+@implementation DDHidAppleRemote
+
++ (NSArray *) allRemotes;
+{
+ CFMutableDictionaryRef hidMatchDictionary =
+ IOServiceMatching("AppleIRController");
+
+ return
+ [DDHidDevice allDevicesMatchingCFDictionary: hidMatchDictionary
+ withClass: self
+ skipZeroLocations: YES];
+}
+
++ (DDHidAppleRemote *) firstRemote;
+{
+ NSArray * allRemotes = [self allRemotes];
+ if ([allRemotes count] > 0)
+ return [allRemotes objectAtIndex: 0];
+ else
+ return nil;
+}
+
+- (id) initWithDevice: (io_object_t) device error: (NSError **) error_;
+{
+ self = [super initWithDevice: device error: error_];
+ if (self == nil)
+ return nil;
+
+ mCookieToButtonMapping = [[NSMutableDictionary alloc] init];
+
+ [mCookieToButtonMapping ddhid_setInt: kDDHidRemoteButtonVolume_Plus
+ forKey: @"14_12_11_6_5_"];
+ [mCookieToButtonMapping ddhid_setInt: kDDHidRemoteButtonVolume_Plus
+ forKey: @"31_29_28_19_18_"];
+ [mCookieToButtonMapping ddhid_setInt: kDDHidRemoteButtonVolume_Minus
+ forKey: @"14_13_11_6_5_"];
+ [mCookieToButtonMapping ddhid_setInt: kDDHidRemoteButtonVolume_Minus
+ forKey: @"31_30_28_19_18_"];
+ [mCookieToButtonMapping ddhid_setInt: kDDHidRemoteButtonMenu
+ forKey: @"14_7_6_5_14_7_6_5_"];
+ [mCookieToButtonMapping ddhid_setInt: kDDHidRemoteButtonMenu
+ forKey: @"31_20_19_18_31_20_19_18_"];
+ [mCookieToButtonMapping ddhid_setInt: kDDHidRemoteButtonPlay
+ forKey: @"14_8_6_5_14_8_6_5_"];
+ [mCookieToButtonMapping ddhid_setInt: kDDHidRemoteButtonPlay
+ forKey: @"31_21_19_18_31_21_19_18_"];
+ [mCookieToButtonMapping ddhid_setInt: kDDHidRemoteButtonRight
+ forKey: @"14_9_6_5_14_9_6_5_"];
+ [mCookieToButtonMapping ddhid_setInt: kDDHidRemoteButtonRight
+ forKey: @"31_22_19_18_31_22_19_18_"];
+ [mCookieToButtonMapping ddhid_setInt: kDDHidRemoteButtonLeft
+ forKey: @"14_10_6_5_14_10_6_5_"];
+ [mCookieToButtonMapping ddhid_setInt: kDDHidRemoteButtonLeft
+ forKey: @"31_23_19_18_31_23_19_18_"];
+ [mCookieToButtonMapping ddhid_setInt: kDDHidRemoteButtonRight_Hold
+ forKey: @"14_6_5_4_2_"];
+ [mCookieToButtonMapping ddhid_setInt: kDDHidRemoteButtonRight_Hold
+ forKey: @"31_19_18_4_2_"];
+ [mCookieToButtonMapping ddhid_setInt: kDDHidRemoteButtonLeft_Hold
+ forKey: @"14_6_5_3_2_"];
+ [mCookieToButtonMapping ddhid_setInt: kDDHidRemoteButtonLeft_Hold
+ forKey: @"31_19_18_3_2_"];
+ [mCookieToButtonMapping ddhid_setInt: kDDHidRemoteButtonMenu_Hold
+ forKey: @"14_6_5_14_6_5_"];
+ [mCookieToButtonMapping ddhid_setInt: kDDHidRemoteButtonMenu_Hold
+ forKey: @"31_19_18_31_19_18_"];
+ [mCookieToButtonMapping ddhid_setInt: kDDHidRemoteButtonPlay_Sleep
+ forKey: @"18_14_6_5_18_14_6_5_"];
+ [mCookieToButtonMapping ddhid_setInt: kDDHidRemoteButtonPlay_Sleep
+ forKey: @"35_31_19_18_35_31_19_18_"];
+ [mCookieToButtonMapping ddhid_setInt: kDDHidRemoteControl_Switched
+ forKey: @"19_"];
+ //[mCookieToButtonMapping ddhid_setInt: kDDHidRemoteControl_Switched
+ // forKey: @"??_"]; // unknown for 10.5
+ [mCookieToButtonMapping ddhid_setInt: kDDHidRemoteControl_Paired
+ forKey: @"15_14_6_5_15_14_6_5_"];
+ [mCookieToButtonMapping ddhid_setInt: kDDHidRemoteControl_Paired
+ forKey: @"32_31_19_18_32_31_19_18_"];
+
+ [self initRemoteElements: [self elements]];
+
+
+ return self;
+}
+
+//===========================================================
+// dealloc
+//===========================================================
+- (void) dealloc
+{
+ [mCookieToButtonMapping release];
+ [mButtonElements release];
+ [mIdElement release];
+
+ mCookieToButtonMapping = nil;
+ mButtonElements = nil;
+ mIdElement = nil;
+ [super dealloc];
+}
+
+#pragma mark -
+#pragma mark Asynchronous Notification
+
+- (void) setDelegate: (id) delegate;
+{
+ mDelegate = delegate;
+}
+
+- (void) addElementsToDefaultQueue;
+{
+ [mDefaultQueue addElements: mButtonElements];
+}
+
+#pragma mark -
+#pragma mark Properties
+
+//===========================================================
+// remoteId
+//===========================================================
+- (int) remoteId
+{
+ return mRemoteId;
+}
+
+- (void) setRemoteId: (int) theRemoteId
+{
+ mRemoteId = theRemoteId;
+}
+
+@end
+
+@implementation DDHidAppleRemote (Private)
+
+- (void) initRemoteElements: (NSArray *) elements;
+{
+ NSAssert([elements count] == 1, @"Assume only 1 top level remote element");
+ DDHidElement * consumerControlsElement = [elements objectAtIndex: 0];
+ mButtonElements = [[consumerControlsElement elements] retain];
+ DDHidElement * element;
+ NSEnumerator * e = [mButtonElements objectEnumerator];
+ while (element = [e nextObject])
+ {
+ if ([[element usage] isEqualToUsagePage: 0x0006
+ usageId: 0x0022])
+ {
+ mIdElement = [element retain];
+ }
+ }
+}
+
+- (void) ddhidQueueHasEvents: (DDHidQueue *) hidQueue;
+{
+ NSMutableString * cookieString = [NSMutableString string];
+ SInt32 sumOfValues = 0;
+
+ DDHidEvent * event;
+ while (event = [hidQueue nextEvent])
+ {
+ if ([event elementCookie] == [mIdElement cookie])
+ {
+ [self setRemoteId: [event value]];
+ }
+ else
+ {
+ sumOfValues += [event value];
+ [cookieString appendString:
+ [NSString stringWithFormat: @"%u_", [event elementCookieAsUnsigned]]];
+ }
+ }
+ [self handleEventWithCookieString: cookieString sumOfValues: sumOfValues];
+}
+
+- (void) handleEventWithCookieString: (NSString*) cookieString
+ sumOfValues: (SInt32) sumOfValues;
+{
+ NSNumber* buttonId = [mCookieToButtonMapping objectForKey: cookieString];
+ if (buttonId != nil)
+ {
+ if ([mDelegate respondsToSelector: @selector(ddhidAppleRemoteButton:pressedDown:)])
+ {
+ [mDelegate ddhidAppleRemoteButton: [buttonId intValue]
+ pressedDown: (sumOfValues>0)];
+ }
+ }
+ else
+ NSLog(@"Unknown button for cookiestring %@", cookieString);
+
+}
+
+@end
+
diff --git a/lib/DDHidDevice.h b/lib/DDHidDevice.h
new file mode 100644
index 0000000..40b9fdb
--- /dev/null
+++ b/lib/DDHidDevice.h
@@ -0,0 +1,131 @@
+/*
+ * Copyright (c) 2007 Dave Dribin
+ *
+ * Permission is hereby granted, free of charge, to any person
+ * obtaining a copy of this software and associated documentation
+ * files (the "Software"), to deal in the Software without
+ * restriction, including without limitation the rights to use, copy,
+ * modify, merge, publish, distribute, sublicense, and/or sell copies
+ * of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be
+ * included in all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+ * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
+ * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
+ * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
+ * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+ * SOFTWARE.
+ */
+
+#import <Cocoa/Cocoa.h>
+#include <IOKit/IOKitLib.h>
+#include <IOKit/IOCFPlugIn.h>
+#include <IOKit/hid/IOHIDLib.h>
+#include <IOKit/hid/IOHIDKeys.h>
+
+@class DDHidUsage;
+@class DDHidElement;
+@class DDHidQueue;
+
+@interface DDHidDevice : NSObject
+{
+ io_object_t mHidDevice;
+ IOHIDDeviceInterface122** mDeviceInterface;
+
+ NSMutableDictionary * mProperties;
+ DDHidUsage * mPrimaryUsage;
+ NSMutableArray * mUsages;
+ NSArray * mElements;
+ NSMutableDictionary * mElementsByCookie;
+ BOOL mListenInExclusiveMode;
+ DDHidQueue * mDefaultQueue;
+ int mTag;
+ int mLogicalDeviceNumber;
+}
+
+- (id) initWithDevice: (io_object_t) device error: (NSError **) error;
+- (id) initLogicalWithDevice: (io_object_t) device
+ logicalDeviceNumber: (int) logicalDeviceNumber
+ error: (NSError **) error;
+- (int) logicalDeviceCount;
+
+#pragma mark -
+#pragma mark Finding Devices
+
++ (NSArray *) allDevices;
+
++ (NSArray *) allDevicesMatchingUsagePage: (unsigned) usagePage
+ usageId: (unsigned) usageId
+ withClass: (Class) hidClass
+ skipZeroLocations: (BOOL) emptyLocation;
+
++ (NSArray *) allDevicesMatchingCFDictionary: (CFDictionaryRef) matchDictionary
+ withClass: (Class) hidClass
+ skipZeroLocations: (BOOL) emptyLocation;
+
+#pragma mark -
+#pragma mark I/O Kit Objects
+
+- (io_object_t) ioDevice;
+- (IOHIDDeviceInterface122**) deviceInterface;
+
+#pragma mark -
+#pragma mark Operations
+
+- (void) open;
+- (void) openWithOptions: (UInt32) options;
+- (void) close;
+- (DDHidQueue *) createQueueWithSize: (unsigned) size;
+- (long) getElementValue: (DDHidElement *) element;
+
+#pragma mark -
+#pragma mark Asynchronous Notification
+
+- (BOOL) listenInExclusiveMode;
+- (void) setListenInExclusiveMode: (BOOL) flag;
+
+- (void) startListening;
+
+- (void) stopListening;
+
+- (BOOL) isListening;
+
+#pragma mark -
+#pragma mark Properties
+
+- (NSDictionary *) properties;
+
+- (NSArray *) elements;
+- (DDHidElement *) elementForCookie: (IOHIDElementCookie) cookie;
+
+- (NSString *) productName;
+- (NSString *) manufacturer;
+- (NSString *) serialNumber;
+- (NSString *) transport;
+- (long) vendorId;
+- (long) productId;
+- (long) version;
+- (long) locationId;
+- (long) usagePage;
+- (long) usage;
+- (DDHidUsage *) primaryUsage;
+- (NSArray *) usages;
+
+- (NSComparisonResult) compareByLocationId: (DDHidDevice *) device;
+
+- (int) tag;
+- (void) setTag: (int) theTag;
+
+@end
+
+@interface DDHidDevice (Protected)
+
+- (unsigned) sizeOfDefaultQueue;
+- (void) addElementsToDefaultQueue;
+
+@end
diff --git a/lib/DDHidDevice.m b/lib/DDHidDevice.m
new file mode 100644
index 0000000..8000185
--- /dev/null
+++ b/lib/DDHidDevice.m
@@ -0,0 +1,585 @@
+/*
+ * Copyright (c) 2007 Dave Dribin
+ *
+ * Permission is hereby granted, free of charge, to any person
+ * obtaining a copy of this software and associated documentation
+ * files (the "Software"), to deal in the Software without
+ * restriction, including without limitation the rights to use, copy,
+ * modify, merge, publish, distribute, sublicense, and/or sell copies
+ * of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be
+ * included in all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+ * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
+ * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
+ * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
+ * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+ * SOFTWARE.
+ */
+
+#import "DDHidDevice.h"
+#import "DDHidUsage.h"
+#import "DDHidElement.h"
+#import "DDHidQueue.h"
+#import "NSDictionary+DDHidExtras.h"
+#import "NSXReturnThrowError.h"
+
+#include <IOKit/hid/IOHIDUsageTables.h>
+
+@interface DDHidDevice (Private)
+
++ (void) addDevice: (io_object_t) hidDevice
+ withClass: (Class) hidClass
+ skipZeroLocations: (BOOL) skipZeroLocations
+ toDeviceList: (NSMutableArray *) devices;
+
+- (BOOL) initPropertiesWithError: (NSError **) error_;
+- (BOOL) createDeviceInterfaceWithError: (NSError **) error_;
+
+@end
+
+@implementation DDHidDevice
+
+- (id) initWithDevice: (io_object_t) device error: (NSError **) error;
+{
+ return [self initLogicalWithDevice: device
+ logicalDeviceNumber: 0
+ error: error];
+}
+
+- (id) initLogicalWithDevice: (io_object_t) device
+ logicalDeviceNumber: (int) logicalDeviceNumber
+ error: (NSError **) error;
+{
+ self = [super init];
+ if (self == nil)
+ return nil;
+
+ mHidDevice = device;
+ IOObjectRetain(mHidDevice);
+
+ if (![self initPropertiesWithError: error])
+ {
+ [self release];
+ return nil;
+ }
+
+ if (![self createDeviceInterfaceWithError: error])
+ {
+ [self release];
+ return nil;
+ }
+
+ mLogicalDeviceNumber = logicalDeviceNumber;
+ mListenInExclusiveMode = NO;
+ mDefaultQueue = nil;
+ mTag = 0;
+
+ return self;
+}
+
+//===========================================================
+// dealloc
+//===========================================================
+- (void) dealloc
+{
+ [mDefaultQueue release];
+ if (mDeviceInterface != NULL)
+ {
+ (*mDeviceInterface)->close(mDeviceInterface);
+ (*mDeviceInterface)->Release(mDeviceInterface);
+ }
+ [mElementsByCookie release];
+ [mElements release];
+ [mUsages release];
+ [mPrimaryUsage release];
+ [mProperties release];
+ IOObjectRelease(mHidDevice);
+
+ mProperties = nil;
+ mDeviceInterface = NULL;
+ [super dealloc];
+}
+
+#pragma mark -
+#pragma mark Finding Devices
+
++ (NSArray *) allDevices;
+{
+ // Set up a matching dictionary to search the I/O Registry by class
+ // name for all HID class devices
+ CFMutableDictionaryRef hidMatchDictionary =
+ IOServiceMatching(kIOHIDDeviceKey);
+ return [self allDevicesMatchingCFDictionary: hidMatchDictionary
+ withClass: [DDHidDevice class]
+ skipZeroLocations: NO];
+}
+
++ (NSArray *) allDevicesMatchingUsagePage: (unsigned) usagePage
+ usageId: (unsigned) usageId
+ withClass: (Class) hidClass
+ skipZeroLocations: (BOOL) skipZeroLocations;
+{
+ // Set up a matching dictionary to search the I/O Registry by class
+ // name for all HID class devices
+ CFMutableDictionaryRef hidMatchDictionary =
+ IOServiceMatching(kIOHIDDeviceKey);
+ NSMutableDictionary * objcMatchDictionary =
+ (NSMutableDictionary *) hidMatchDictionary;
+ [objcMatchDictionary ddhid_setObject: [NSNumber numberWithUnsignedInt: usagePage]
+ forString: kIOHIDDeviceUsagePageKey];
+ [objcMatchDictionary ddhid_setObject: [NSNumber numberWithUnsignedInt: usageId]
+ forString: kIOHIDDeviceUsageKey];
+ return [self allDevicesMatchingCFDictionary: hidMatchDictionary
+ withClass: hidClass
+ skipZeroLocations: skipZeroLocations];
+}
+
++ (NSArray *) allDevicesMatchingCFDictionary: (CFDictionaryRef) matchDictionary
+ withClass: (Class) hidClass
+ skipZeroLocations: (BOOL) skipZeroLocations;
+{
+ // Now search I/O Registry for matching devices.
+ io_iterator_t hidObjectIterator = MACH_PORT_NULL;
+ NSMutableArray * devices = [NSMutableArray array];
+ @try
+ {
+ NSXThrowError(IOServiceGetMatchingServices(kIOMasterPortDefault,
+ matchDictionary,
+ &hidObjectIterator));
+
+ if (hidObjectIterator == 0)
+ return [NSArray array];
+
+ io_object_t hidDevice;
+ while (hidDevice = IOIteratorNext(hidObjectIterator))
+ {
+ [self addDevice: hidDevice
+ withClass: hidClass
+ skipZeroLocations: skipZeroLocations
+ toDeviceList: devices];
+ }
+
+ // This makes sure the array return is consistent from run to run,
+ // assuming no new devices were added.
+ [devices sortUsingSelector: @selector(compareByLocationId:)];
+ }
+ @finally
+ {
+ if (hidObjectIterator != MACH_PORT_NULL)
+ IOObjectRelease(hidObjectIterator);
+ }
+
+ return devices;
+}
+
+- (int) logicalDeviceCount;
+{
+ return 1;
+}
+
+#pragma mark -
+#pragma mark I/O Kit Objects
+
+- (io_object_t) ioDevice;
+{
+ return mHidDevice;
+}
+
+- (IOHIDDeviceInterface122**) deviceInterface;
+{
+ return mDeviceInterface;
+}
+
+#pragma mark -
+#pragma mark Operations
+
+- (void) open;
+{
+ [self openWithOptions: kIOHIDOptionsTypeNone];
+}
+
+- (void) openWithOptions: (UInt32) options;
+{
+ NSXThrowError((*mDeviceInterface)->open(mDeviceInterface, options));
+}
+
+- (void) close;
+{
+ NSXThrowError((*mDeviceInterface)->close(mDeviceInterface));
+}
+
+- (DDHidQueue *) createQueueWithSize: (unsigned) size;
+{
+ IOHIDQueueInterface ** queue =
+ (*mDeviceInterface)->allocQueue(mDeviceInterface);
+ if (queue == NULL)
+ return nil;
+ return [[[DDHidQueue alloc] initWithHIDQueue: queue
+ size: size] autorelease];
+}
+
+- (long) getElementValue: (DDHidElement *) element;
+{
+ IOHIDEventStruct event;
+ NSXThrowError((*mDeviceInterface)->getElementValue(mDeviceInterface,
+ [element cookie],
+ &event));
+ return event.value;
+}
+
+#pragma mark -
+#pragma mark Asynchronous Notification
+
+//===========================================================
+// listenInExclusiveMode
+//===========================================================
+- (BOOL) listenInExclusiveMode
+{
+ return mListenInExclusiveMode;
+}
+
+- (void) setListenInExclusiveMode: (BOOL) flag
+{
+ mListenInExclusiveMode = flag;
+}
+
+- (void) startListening;
+{
+ if ([self isListening])
+ return;
+
+ UInt32 options = kIOHIDOptionsTypeNone;
+ if (mListenInExclusiveMode)
+ options = kIOHIDOptionsTypeSeizeDevice;
+ [self openWithOptions: options];
+ mDefaultQueue = [[self createQueueWithSize: [self sizeOfDefaultQueue]] retain];
+ [mDefaultQueue setDelegate: self];
+ [self addElementsToDefaultQueue];
+ [mDefaultQueue startOnCurrentRunLoop];
+}
+
+- (void) stopListening;
+{
+ if (![self isListening])
+ return;
+
+ [mDefaultQueue stop];
+ [mDefaultQueue release];
+ mDefaultQueue = nil;
+ [self close];
+}
+
+- (BOOL) isListening;
+{
+ return (mDefaultQueue != nil);
+}
+
+#pragma mark -
+#pragma mark Properties
+
+- (NSDictionary *) properties;
+{
+ return mProperties;
+}
+
+//===========================================================
+// - productName
+//===========================================================
+- (NSString *) productName
+{
+ NSString * productName = [mProperties ddhid_stringForString: kIOHIDProductKey];
+ if ([self logicalDeviceCount] > 1)
+ {
+ productName = [productName stringByAppendingString:
+ [NSString stringWithFormat:@" #%d", mLogicalDeviceNumber + 1]];
+ }
+ return productName;
+}
+
+//===========================================================
+// - manufacturer
+//===========================================================
+- (NSString *) manufacturer
+{
+ return [mProperties ddhid_stringForString: kIOHIDManufacturerKey];
+}
+
+//===========================================================
+// - serialNumber
+//===========================================================
+- (NSString *) serialNumber
+{
+ return [mProperties ddhid_stringForString: kIOHIDSerialNumberKey];
+}
+
+//===========================================================
+// - transport
+//===========================================================
+- (NSString *) transport
+{
+ return [mProperties ddhid_stringForString: kIOHIDTransportKey];
+}
+
+//===========================================================
+// - vendorId
+//===========================================================
+- (long) vendorId
+{
+ return [mProperties ddhid_longForString: kIOHIDVendorIDKey];
+}
+
+//===========================================================
+// - productId
+//===========================================================
+- (long) productId
+{
+ return [mProperties ddhid_longForString: kIOHIDProductIDKey];
+}
+
+//===========================================================
+// - version
+//===========================================================
+- (long) version
+{
+ return [mProperties ddhid_longForString: kIOHIDVersionNumberKey];
+}
+
+//===========================================================
+// - locationId
+//===========================================================
+- (long) locationId
+{
+ return [mProperties ddhid_longForString: kIOHIDLocationIDKey];
+}
+
+//===========================================================
+// - usagePage
+//===========================================================
+- (long) usagePage
+{
+ return [mProperties ddhid_longForString: kIOHIDPrimaryUsagePageKey];
+}
+
+//===========================================================
+// - usage
+//===========================================================
+- (long) usage
+{
+ return [mProperties ddhid_longForString: kIOHIDPrimaryUsageKey];
+}
+
+- (NSArray *) elements;
+{
+ return mElements;
+}
+
+- (DDHidElement *) elementForCookie: (IOHIDElementCookie) cookie;
+{
+ NSNumber * n = [NSNumber numberWithUnsignedInt: (unsigned) cookie];
+ return [mElementsByCookie objectForKey: n];
+}
+
+- (DDHidUsage *) primaryUsage;
+{
+ return mPrimaryUsage;
+}
+
+- (NSArray *) usages;
+{
+ return mUsages;
+}
+
+- (NSComparisonResult) compareByLocationId: (DDHidDevice *) device;
+{
+ long myLocationId = [self locationId];
+ long otherLocationId = [device locationId];
+ if (myLocationId < otherLocationId)
+ return NSOrderedAscending;
+ else if (myLocationId > otherLocationId)
+ return NSOrderedDescending;
+ else
+ return NSOrderedSame;
+}
+
+//===========================================================
+// tag
+//===========================================================
+- (int) tag
+{
+ return mTag;
+}
+
+- (void) setTag: (int) theTag
+{
+ mTag = theTag;
+}
+
+@end
+
+@implementation DDHidDevice (Protected)
+
+- (unsigned) sizeOfDefaultQueue;
+{
+ return 10;
+}
+
+- (void) addElementsToDefaultQueue;
+{
+ [mDefaultQueue addElements: [self elements] recursively: YES];
+}
+
+@end
+
+@implementation DDHidDevice (Private)
+
++ (void) addDevice: (io_object_t) hidDevice
+ withClass: (Class) hidClass
+ skipZeroLocations: (BOOL) skipZeroLocations
+ toDeviceList: (NSMutableArray *) devices;
+{
+ @try
+ {
+ NSError * error = nil;
+ DDHidDevice * device = [[hidClass alloc] initWithDevice: hidDevice
+ error: &error];
+ if (device == nil)
+ {
+ NSXRaiseError(error);
+ }
+ [device autorelease];
+
+ if (([device locationId] == 0) && skipZeroLocations)
+ return;
+
+ [devices addObject: device];
+
+ // Add remainnig logical devices
+ int i;
+ for (i = 1; i < [device logicalDeviceCount]; i++)
+ {
+ device = [[hidClass alloc] initLogicalWithDevice: hidDevice
+ logicalDeviceNumber: i
+ error: &error];
+
+ if (device == nil)
+ {
+ NSXRaiseError(error);
+ }
+ [device autorelease];
+
+ [devices addObject: device];
+ }
+ }
+ @finally
+ {
+ IOObjectRelease(hidDevice);
+ }
+}
+
+- (void) indexElements: (NSArray *) elements;
+{
+ NSEnumerator * e = [elements objectEnumerator];
+ DDHidElement * element;
+ while (element = [e nextObject])
+ {
+ NSNumber * n = [NSNumber numberWithUnsignedInt: [element cookieAsUnsigned]];
+ [mElementsByCookie setObject: element
+ forKey: n];
+ NSArray * children = [element elements];
+ if (children != nil)
+ [self indexElements: children];
+ }
+}
+
+- (BOOL) initPropertiesWithError: (NSError **) error_;
+{
+ NSError * error = nil;
+ BOOL result = NO;
+
+ CFMutableDictionaryRef properties;
+ NSXReturnError(IORegistryEntryCreateCFProperties(mHidDevice, &properties,
+ kCFAllocatorDefault, kNilOptions));
+ if (error)
+ goto done;
+
+ mProperties = (NSMutableDictionary *) properties;
+ NSArray * elementProperties = [mProperties ddhid_objectForString: kIOHIDElementKey];
+ mElements = [DDHidElement elementsWithPropertiesArray: elementProperties];
+ [mElements retain];
+
+ unsigned usagePage = [mProperties ddhid_unsignedIntForString: kIOHIDPrimaryUsagePageKey];
+ unsigned usageId = [mProperties ddhid_unsignedIntForString: kIOHIDPrimaryUsageKey];
+
+ mPrimaryUsage = [[DDHidUsage alloc] initWithUsagePage: usagePage
+ usageId: usageId];
+ mUsages = [[NSMutableArray alloc] init];
+
+ NSArray * usagePairs = [mProperties ddhid_objectForString: kIOHIDDeviceUsagePairsKey];
+ NSEnumerator * e = [usagePairs objectEnumerator];
+ NSDictionary * usagePair;
+ while (usagePair = [e nextObject])
+ {
+ usagePage = [usagePair ddhid_unsignedIntForString: kIOHIDDeviceUsagePageKey];
+ usageId = [usagePair ddhid_unsignedIntForString: kIOHIDDeviceUsageKey];
+ DDHidUsage * usage = [DDHidUsage usageWithUsagePage: usagePage
+ usageId: usageId];
+ [mUsages addObject: usage];
+ }
+
+ mElementsByCookie = [[NSMutableDictionary alloc] init];
+ [self indexElements: mElements];
+ result = YES;
+
+done:
+ if (error_)
+ *error_ = error;
+ return result;
+}
+
+- (BOOL) createDeviceInterfaceWithError: (NSError **) error_;
+{
+ io_name_t className;
+ IOCFPlugInInterface ** plugInInterface = NULL;
+ SInt32 score = 0;
+ NSError * error = nil;
+ BOOL result = NO;
+
+ mDeviceInterface = NULL;
+
+ NSXReturnError(IOObjectGetClass(mHidDevice, className));
+ if (error)
+ goto done;
+
+ NSXReturnError(IOCreatePlugInInterfaceForService(mHidDevice,
+ kIOHIDDeviceUserClientTypeID,
+ kIOCFPlugInInterfaceID,
+ &plugInInterface,
+ &score));
+ if (error)
+ goto done;
+
+ //Call a method of the intermediate plug-in to create the device interface
+ NSXReturnError((*plugInInterface)->QueryInterface(plugInInterface, CFUUIDGetUUIDBytes(kIOHIDDeviceInterfaceID), (LPVOID) &mDeviceInterface));
+ if (error)
+ goto done;
+
+ result = YES;
+
+done:
+ if (plugInInterface != NULL)
+ {
+ (*plugInInterface)->Release(plugInInterface);
+ }
+ if (error_)
+ *error_ = error;
+ return result;
+}
+
+@end
+
diff --git a/lib/DDHidElement.h b/lib/DDHidElement.h
new file mode 100644
index 0000000..4e9577c
--- /dev/null
+++ b/lib/DDHidElement.h
@@ -0,0 +1,65 @@
+/*
+ * Copyright (c) 2007 Dave Dribin
+ *
+ * Permission is hereby granted, free of charge, to any person
+ * obtaining a copy of this software and associated documentation
+ * files (the "Software"), to deal in the Software without
+ * restriction, including without limitation the rights to use, copy,
+ * modify, merge, publish, distribute, sublicense, and/or sell copies
+ * of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be
+ * included in all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+ * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
+ * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
+ * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
+ * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+ * SOFTWARE.
+ */
+
+#import <Cocoa/Cocoa.h>
+#include <IOKit/hid/IOHIDKeys.h>
+
+@class DDHidUsage;
+
+@interface DDHidElement : NSObject
+{
+ NSDictionary * mProperties;
+ DDHidUsage * mUsage;
+ NSArray * mElements;
+}
+
++ (NSArray *) elementsWithPropertiesArray: (NSArray *) propertiesArray;
+
++ (DDHidElement *) elementWithProperties: (NSDictionary *) properties;
+
+- (id) initWithProperties: (NSDictionary *) properties;
+
+- (NSDictionary *) properties;
+
+- (NSString *) stringForKey: (NSString *) key;
+
+- (NSString *) description;
+
+- (IOHIDElementCookie) cookie;
+- (unsigned) cookieAsUnsigned;
+
+- (NSArray *) elements;
+- (DDHidUsage *) usage;
+- (NSString *) name;
+- (BOOL) hasNullState;
+- (BOOL) hasPreferredState;
+- (BOOL) isArray;
+- (BOOL) isRelative;
+- (BOOL) isWrapping;
+- (long) maxValue;
+- (long) minValue;
+
+- (NSComparisonResult) compareByUsage: (DDHidElement *) device;
+
+@end
diff --git a/lib/DDHidElement.m b/lib/DDHidElement.m
new file mode 100644
index 0000000..5a00095
--- /dev/null
+++ b/lib/DDHidElement.m
@@ -0,0 +1,185 @@
+/*
+ * Copyright (c) 2007 Dave Dribin
+ *
+ * Permission is hereby granted, free of charge, to any person
+ * obtaining a copy of this software and associated documentation
+ * files (the "Software"), to deal in the Software without
+ * restriction, including without limitation the rights to use, copy,
+ * modify, merge, publish, distribute, sublicense, and/or sell copies
+ * of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be
+ * included in all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+ * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
+ * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
+ * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
+ * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+ * SOFTWARE.
+ */
+
+#import "DDHidElement.h"
+#import "DDHidUsage.h"
+#import "NSDictionary+DDHidExtras.h"
+#include <IOKit/hid/IOHIDKeys.h>
+
+@implementation DDHidElement
+
++ (NSArray *) elementsWithPropertiesArray: (NSArray *) propertiesArray;
+{
+ NSMutableArray * elements = [NSMutableArray array];
+
+ NSDictionary * properties;
+ NSEnumerator * e = [propertiesArray objectEnumerator];
+ while (properties = [e nextObject])
+ {
+ DDHidElement * element = [DDHidElement elementWithProperties: properties];
+ [elements addObject: element];
+ }
+
+ return elements;
+}
+
++ (DDHidElement *) elementWithProperties: (NSDictionary *) properties;
+{
+ DDHidElement * element = [[DDHidElement alloc] initWithProperties: properties];
+ return [element autorelease];
+}
+
+- (id) initWithProperties: (NSDictionary *) properties;
+{
+ self = [super init];
+ if (self == nil)
+ return nil;
+
+ mProperties = [properties retain];
+ unsigned usagePage = [mProperties ddhid_unsignedIntForString: kIOHIDElementUsagePageKey];
+ unsigned usageId = [mProperties ddhid_unsignedIntForString: kIOHIDElementUsageKey];
+ mUsage = [[DDHidUsage alloc] initWithUsagePage: usagePage
+ usageId: usageId];
+
+ NSArray * elementsProperties =
+ [mProperties ddhid_objectForString: kIOHIDElementKey];
+ mElements = [[DDHidElement elementsWithPropertiesArray: elementsProperties]
+ retain];
+
+ return self;
+}
+
+//===========================================================
+// dealloc
+//===========================================================
+- (void) dealloc
+{
+ [mProperties release];
+ [mUsage release];
+ [mElements release];
+
+ mProperties = nil;
+ mUsage = nil;
+ mElements = nil;
+ [super dealloc];
+}
+
+- (NSDictionary *) properties;
+{
+ return mProperties;
+}
+
+- (NSString *) stringForKey: (NSString *) key;
+{
+ return [mProperties objectForKey: key];
+}
+
+- (NSString *) description;
+{
+ return [[self usage] usageNameWithIds];
+}
+
+- (IOHIDElementCookie) cookie;
+{
+ return (IOHIDElementCookie)
+ [mProperties ddhid_unsignedIntForString: kIOHIDElementCookieKey];
+}
+
+- (unsigned) cookieAsUnsigned;
+{
+ return [mProperties ddhid_unsignedIntForString: kIOHIDElementCookieKey];
+}
+
+- (DDHidUsage *) usage;
+{
+ return mUsage;
+}
+
+- (NSArray *) elements;
+{
+ return mElements;
+}
+
+- (NSString *) name;
+{
+ return [mProperties ddhid_objectForString: kIOHIDElementNameKey];
+}
+
+- (BOOL) hasNullState;
+{
+ return [mProperties ddhid_boolForString: kIOHIDElementHasNullStateKey];
+}
+
+- (BOOL) hasPreferredState;
+{
+ return [mProperties ddhid_boolForString: kIOHIDElementHasNullStateKey];
+}
+
+- (BOOL) isArray;
+{
+ return [mProperties ddhid_boolForString: kIOHIDElementIsArrayKey];
+}
+
+- (BOOL) isRelative;
+{
+ return [mProperties ddhid_boolForString: kIOHIDElementIsRelativeKey];
+}
+
+- (BOOL) isWrapping;
+{
+ return [mProperties ddhid_boolForString: kIOHIDElementIsWrappingKey];
+}
+
+- (long) maxValue;
+{
+ return [mProperties ddhid_longForString: kIOHIDElementMaxKey];
+}
+
+- (long) minValue;
+{
+ return [mProperties ddhid_longForString: kIOHIDElementMinKey];
+}
+
+- (NSComparisonResult) compareByUsage: (DDHidElement *) device;
+{
+ unsigned myUsagePage = [[self usage] usagePage];
+ unsigned myUsageId = [[self usage] usageId];
+
+ unsigned otherUsagePage = [[device usage] usagePage];
+ unsigned otherUsageId = [[device usage] usageId];
+
+ if (myUsagePage < otherUsagePage)
+ return NSOrderedAscending;
+ else if (myUsagePage > otherUsagePage)
+ return NSOrderedDescending;
+
+ if (myUsageId < otherUsageId)
+ return NSOrderedAscending;
+ else if (myUsageId > otherUsageId)
+ return NSOrderedDescending;
+
+ return NSOrderedSame;
+}
+
+@end
diff --git a/lib/DDHidEvent.h b/lib/DDHidEvent.h
new file mode 100644
index 0000000..0861cfd
--- /dev/null
+++ b/lib/DDHidEvent.h
@@ -0,0 +1,45 @@
+/*
+ * Copyright (c) 2007 Dave Dribin
+ *
+ * Permission is hereby granted, free of charge, to any person
+ * obtaining a copy of this software and associated documentation
+ * files (the "Software"), to deal in the Software without
+ * restriction, including without limitation the rights to use, copy,
+ * modify, merge, publish, distribute, sublicense, and/or sell copies
+ * of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be
+ * included in all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+ * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
+ * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
+ * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
+ * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+ * SOFTWARE.
+ */
+
+#import <Cocoa/Cocoa.h>
+#include <IOKit/hid/IOHIDLib.h>
+
+@interface DDHidEvent : NSObject
+{
+ IOHIDEventStruct mEvent;
+}
+
++ (DDHidEvent *) eventWithIOHIDEvent: (IOHIDEventStruct *) event;
+
+- (id) initWithIOHIDEvent: (IOHIDEventStruct *) event;
+
+- (IOHIDElementType) type;
+- (IOHIDElementCookie) elementCookie;
+- (unsigned) elementCookieAsUnsigned;
+- (SInt32) value;
+- (AbsoluteTime) timestamp;
+- (UInt32) longValueSize;
+- (void *) longValue;
+
+@end
diff --git a/lib/DDHidEvent.m b/lib/DDHidEvent.m
new file mode 100644
index 0000000..8b2ab5f
--- /dev/null
+++ b/lib/DDHidEvent.m
@@ -0,0 +1,81 @@
+/*
+ * Copyright (c) 2007 Dave Dribin
+ *
+ * Permission is hereby granted, free of charge, to any person
+ * obtaining a copy of this software and associated documentation
+ * files (the "Software"), to deal in the Software without
+ * restriction, including without limitation the rights to use, copy,
+ * modify, merge, publish, distribute, sublicense, and/or sell copies
+ * of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be
+ * included in all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+ * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
+ * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
+ * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
+ * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+ * SOFTWARE.
+ */
+
+#import "DDHidEvent.h"
+
+
+@implementation DDHidEvent
+
++ (DDHidEvent *) eventWithIOHIDEvent: (IOHIDEventStruct *) event;
+{
+ return [[[self alloc] initWithIOHIDEvent: event] autorelease];
+}
+
+- (id) initWithIOHIDEvent: (IOHIDEventStruct *) event;
+{
+ self = [super init];
+ if (self == nil)
+ return nil;
+
+ mEvent = *event;
+
+ return self;
+}
+
+- (IOHIDElementType) type;
+{
+ return mEvent.type;
+}
+
+- (IOHIDElementCookie) elementCookie;
+{
+ return mEvent.elementCookie;
+}
+
+- (unsigned) elementCookieAsUnsigned;
+{
+ return (unsigned) mEvent.elementCookie;
+}
+
+- (SInt32) value;
+{
+ return mEvent.value;
+}
+
+- (AbsoluteTime) timestamp;
+{
+ return mEvent.timestamp;
+}
+
+- (UInt32) longValueSize;
+{
+ return mEvent.longValueSize;
+}
+
+- (void *) longValue;
+{
+ return mEvent.longValue;
+}
+
+@end
diff --git a/lib/DDHidJoystick.h b/lib/DDHidJoystick.h
new file mode 100644
index 0000000..ebf20cc
--- /dev/null
+++ b/lib/DDHidJoystick.h
@@ -0,0 +1,132 @@
+/*
+ * Copyright (c) 2007 Dave Dribin
+ *
+ * Permission is hereby granted, free of charge, to any person
+ * obtaining a copy of this software and associated documentation
+ * files (the "Software"), to deal in the Software without
+ * restriction, including without limitation the rights to use, copy,
+ * modify, merge, publish, distribute, sublicense, and/or sell copies
+ * of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be
+ * included in all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+ * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
+ * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
+ * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
+ * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+ * SOFTWARE.
+ */
+
+#import <Cocoa/Cocoa.h>
+#import "DDHidDevice.h"
+
+@class DDHidElement;
+@class DDHidQueue;
+
+@interface DDHidJoystickStick : NSObject
+{
+ DDHidElement * mXAxisElement;
+ DDHidElement * mYAxisElement;
+ NSMutableArray * mStickElements;
+ // Point of view elements (i.e. hat switches)
+ NSMutableArray * mPovElements;
+}
+
+- (DDHidElement *) xAxisElement;
+
+- (DDHidElement *) yAxisElement;
+
+#pragma mark -
+#pragma mark StickElements - indexed accessors
+
+- (unsigned int) countOfStickElements;
+- (DDHidElement *) objectInStickElementsAtIndex: (unsigned int)index;
+
+#pragma mark -
+#pragma mark PovElements - indexed accessors
+
+- (unsigned int) countOfPovElements;
+- (DDHidElement *) objectInPovElementsAtIndex: (unsigned int)index;
+
+- (NSArray *) allElements;
+
+- (BOOL) addElement: (DDHidElement *) element;
+
+@end
+
+@interface DDHidJoystick : DDHidDevice
+{
+ NSMutableArray * mSticks;
+ NSMutableArray * mButtonElements;
+ NSMutableArray * mLogicalDeviceElements;
+
+ id mDelegate;
+}
+
++ (NSArray *) allJoysticks;
+
+- (id) initLogicalWithDevice: (io_object_t) device
+ logicalDeviceNumber: (int) logicalDeviceNumber
+ error: (NSError **) error;
+
+- (int) logicalDeviceCount;
+
+#pragma mark -
+#pragma mark Joystick Elements
+
+- (unsigned) numberOfButtons;
+
+- (NSArray *) buttonElements;
+
+#pragma mark -
+#pragma mark Sticks - indexed accessors
+
+- (unsigned int) countOfSticks;
+- (DDHidJoystickStick *) objectInSticksAtIndex: (unsigned int)index;
+
+- (void) addElementsToQueue: (DDHidQueue *) queue;
+
+#pragma mark -
+#pragma mark Asynchronous Notification
+
+- (void) setDelegate: (id) delegate;
+
+- (void) addElementsToDefaultQueue;
+
+@end
+
+#define DDHID_JOYSTICK_VALUE_MIN -65536
+#define DDHID_JOYSTICK_VALUE_MAX 65536
+
+@interface NSObject (DDHidJoystickDelegate)
+
+- (void) ddhidJoystick: (DDHidJoystick *) joystick
+ stick: (unsigned) stick
+ xChanged: (int) value;
+
+- (void) ddhidJoystick: (DDHidJoystick *) joystick
+ stick: (unsigned) stick
+ yChanged: (int) value;
+
+- (void) ddhidJoystick: (DDHidJoystick *) joystick
+ stick: (unsigned) stick
+ otherAxis: (unsigned) otherAxis
+ valueChanged: (int) value;
+
+- (void) ddhidJoystick: (DDHidJoystick *) joystick
+ stick: (unsigned) stick
+ povNumber: (unsigned) povNumber
+ valueChanged: (int) value;
+
+- (void) ddhidJoystick: (DDHidJoystick *) joystick
+ buttonDown: (unsigned) buttonNumber;
+
+- (void) ddhidJoystick: (DDHidJoystick *) joystick
+ buttonUp: (unsigned) buttonNumber;
+
+@end
diff --git a/lib/DDHidJoystick.m b/lib/DDHidJoystick.m
new file mode 100644
index 0000000..601f9b3
--- /dev/null
+++ b/lib/DDHidJoystick.m
@@ -0,0 +1,654 @@
+/*
+ * Copyright (c) 2007 Dave Dribin
+ *
+ * Permission is hereby granted, free of charge, to any person
+ * obtaining a copy of this software and associated documentation
+ * files (the "Software"), to deal in the Software without
+ * restriction, including without limitation the rights to use, copy,
+ * modify, merge, publish, distribute, sublicense, and/or sell copies
+ * of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be
+ * included in all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+ * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
+ * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
+ * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
+ * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+ * SOFTWARE.
+ */
+
+#import "DDHidLib.h"
+#include <IOKit/hid/IOHIDUsageTables.h>
+
+@interface DDHidJoystick (DDHidJoystickDelegate)
+
+- (void) ddhidJoystick: (DDHidJoystick *) joystick
+ stick: (unsigned) stick
+ xChanged: (int) value;
+
+- (void) ddhidJoystick: (DDHidJoystick *) joystick
+ stick: (unsigned) stick
+ yChanged: (int) value;
+
+- (void) ddhidJoystick: (DDHidJoystick *) joystick
+ stick: (unsigned) stick
+ otherAxis: (unsigned) otherAxis
+ valueChanged: (int) value;
+
+- (void) ddhidJoystick: (DDHidJoystick *) joystick
+ stick: (unsigned) stick
+ povNumber: (unsigned) povNumber
+ valueChanged: (int) value;
+
+- (void) ddhidJoystick: (DDHidJoystick *) joystick
+ buttonDown: (unsigned) buttonNumber;
+
+- (void) ddhidJoystick: (DDHidJoystick *) joystick
+ buttonUp: (unsigned) buttonNumber;
+
+@end
+
+@interface DDHidJoystick (Private)
+
+- (void) initLogicalDeviceElements;
+- (void) initJoystickElements: (NSArray *) elements;
+- (void) addStick: (NSArray *) stickElements;
+- (void) ddhidQueueHasEvents: (DDHidQueue *) hidQueue;
+
+- (int) normalizeValue: (int) value
+ forElement: (DDHidElement *) element;
+
+- (int) povValue: (int) value
+ forElement: (DDHidElement *) element;
+
+- (BOOL) findStick: (unsigned *) stick
+ element: (DDHidElement **) elementOut
+ withXAxisCookie: (IOHIDElementCookie) cookie;
+
+- (BOOL) findStick: (unsigned *) stick
+ element: (DDHidElement **) elementOut
+ withYAxisCookie: (IOHIDElementCookie) cookie;
+
+- (BOOL) findStick: (unsigned *) stickOut
+ otherAxis: (unsigned *) axisOut
+ element: (DDHidElement **) elementOut
+ withCookie: (IOHIDElementCookie) cookie;
+
+- (BOOL) findStick: (unsigned *) stickOut
+ povNumber: (unsigned *) povNumber
+ element: (DDHidElement **) elementOut
+ withCookie: (IOHIDElementCookie) cookie;
+
+@end
+
+@implementation DDHidJoystick
+
++ (NSArray *) allJoysticks;
+{
+ NSArray * joysticks =
+ [DDHidDevice allDevicesMatchingUsagePage: kHIDPage_GenericDesktop
+ usageId: kHIDUsage_GD_Joystick
+ withClass: self
+ skipZeroLocations: YES];
+ NSArray * gamepads =
+ [DDHidDevice allDevicesMatchingUsagePage: kHIDPage_GenericDesktop
+ usageId: kHIDUsage_GD_GamePad
+ withClass: self
+ skipZeroLocations: YES];
+
+ NSMutableArray * allJoysticks = [NSMutableArray arrayWithArray: joysticks];
+ [allJoysticks addObjectsFromArray: gamepads];
+ [allJoysticks sortUsingSelector: @selector(compareByLocationId:)];
+ return allJoysticks;
+}
+
+- (id) initLogicalWithDevice: (io_object_t) device
+ logicalDeviceNumber: (int) logicalDeviceNumber
+ error: (NSError **) error;
+{
+ self = [super initLogicalWithDevice: device
+ logicalDeviceNumber: logicalDeviceNumber
+ error: error];
+ if (self == nil)
+ return nil;
+
+ mButtonElements = [[NSMutableArray alloc] init];
+ mSticks = [[NSMutableArray alloc] init];
+ mLogicalDeviceElements = [[NSMutableArray alloc] init];
+
+ [self initLogicalDeviceElements];
+ int logicalDeviceCount = [mLogicalDeviceElements count];
+ if (logicalDeviceCount == 0)
+ {
+ [self release];
+ return nil;
+ }
+
+ mLogicalDeviceNumber = logicalDeviceNumber;
+ if (mLogicalDeviceNumber >= logicalDeviceCount)
+ mLogicalDeviceNumber = logicalDeviceCount - 1;
+
+ [self initJoystickElements:
+ [mLogicalDeviceElements objectAtIndex: mLogicalDeviceNumber]];
+ [mButtonElements sortUsingSelector: @selector(compareByUsage:)];
+ mDelegate = nil;
+
+ return self;
+}
+
+//===========================================================
+// dealloc
+//===========================================================
+- (void) dealloc
+{
+ [mLogicalDeviceElements release];
+ [mSticks release];
+ [mButtonElements release];
+
+ mLogicalDeviceElements = nil;
+ mSticks = nil;
+ mButtonElements = nil;
+ [super dealloc];
+}
+
+- (int) logicalDeviceCount;
+{
+ return [mLogicalDeviceElements count];
+}
+
+#pragma mark -
+#pragma mark Joystick Elements
+
+//===========================================================
+// - buttonElements
+//===========================================================
+- (NSArray *) buttonElements;
+{
+ return mButtonElements;
+}
+
+- (unsigned) numberOfButtons;
+{
+ return [mButtonElements count];
+}
+
+#pragma mark -
+#pragma mark Sticks - indexed accessors
+
+- (unsigned int) countOfSticks
+{
+ return [mSticks count];
+}
+
+- (DDHidJoystickStick *) objectInSticksAtIndex: (unsigned int)index
+{
+ return [mSticks objectAtIndex: index];
+}
+
+- (void) addElementsToQueue: (DDHidQueue *) queue;
+{
+ NSEnumerator * e = [mSticks objectEnumerator];
+ DDHidJoystickStick * stick;
+ while (stick = [e nextObject])
+ {
+ [queue addElements: [stick allElements]];
+ }
+
+ [queue addElements: mButtonElements];
+}
+
+
+#pragma mark -
+#pragma mark Asynchronous Notification
+
+- (void) setDelegate: (id) delegate;
+{
+ mDelegate = delegate;
+}
+
+- (void) addElementsToDefaultQueue;
+{
+ [self addElementsToQueue: mDefaultQueue];
+}
+
+@end
+
+@implementation DDHidJoystick (Private)
+
+- (void) initLogicalDeviceElements;
+{
+ NSArray * topLevelElements = [self elements];
+ if ([topLevelElements count] == 0)
+ {
+ [mLogicalDeviceElements addObject: topLevelElements];
+ return;
+ }
+
+ NSEnumerator * e = [topLevelElements objectEnumerator];
+ DDHidElement * element;
+ while (element = [e nextObject])
+ {
+ unsigned usagePage = [[element usage] usagePage];
+ unsigned usageId = [[element usage] usageId];
+ if (usagePage == kHIDPage_GenericDesktop &&
+ (usageId == kHIDUsage_GD_Joystick || usageId == kHIDUsage_GD_GamePad))
+ {
+ [mLogicalDeviceElements addObject: [NSArray arrayWithObject: element]];
+ }
+ }
+}
+
+- (void) initJoystickElements: (NSArray *) elements;
+{
+ NSEnumerator * e = [elements objectEnumerator];
+ DDHidElement * element;
+ DDHidJoystickStick * currentStick = [[[DDHidJoystickStick alloc] init] autorelease];
+ BOOL stickHasElements = NO;
+
+ while (element = [e nextObject])
+ {
+ unsigned usagePage = [[element usage] usagePage];
+ unsigned usageId = [[element usage] usageId];
+ NSArray * subElements = [element elements];
+
+ if ([subElements count] > 0)
+ {
+ [self initJoystickElements: subElements];
+ }
+ else if ((usagePage == kHIDPage_GenericDesktop) &&
+ (usageId == kHIDUsage_GD_Pointer))
+ {
+ [self addStick: subElements];
+ }
+ else if ([currentStick addElement: element])
+ {
+ stickHasElements = YES;
+ }
+ else if ((usagePage == kHIDPage_Button) &&
+ (usageId > 0))
+ {
+ [mButtonElements addObject: element];
+ }
+ }
+ if (stickHasElements)
+ {
+ [mSticks addObject: currentStick];
+ }
+}
+
+- (void) addStick: (NSArray *) elements;
+{
+ NSEnumerator * e = [elements objectEnumerator];
+ DDHidElement * element;
+ while (element = [e nextObject])
+ {
+ NSLog(@"Stick element: %@", [[element usage] usageName]);
+ }
+}
+
+- (void) ddhidQueueHasEvents: (DDHidQueue *) hidQueue;
+{
+ DDHidEvent * event;
+ while (event = [hidQueue nextEvent])
+ {
+ IOHIDElementCookie cookie = [event elementCookie];
+ SInt32 value = [event value];
+ DDHidElement * element;
+ unsigned stick;
+ unsigned otherAxis;
+ unsigned povNumber;
+ if ([self findStick: &stick element: &element withXAxisCookie: cookie])
+ {
+ int normalizedValue = [self normalizeValue: value forElement: element];
+ [self ddhidJoystick: self stick: stick xChanged: normalizedValue];
+ }
+ else if ([self findStick: &stick element: &element withYAxisCookie: cookie])
+ {
+ int normalizedValue = [self normalizeValue: value forElement: element];
+ [self ddhidJoystick: self stick: stick yChanged: normalizedValue];
+ }
+ else if ([self findStick: &stick otherAxis: &otherAxis element: &element
+ withCookie: cookie])
+ {
+ int normalizedValue = [self normalizeValue: value forElement: element];
+ [self ddhidJoystick: self stick: stick
+ otherAxis: otherAxis valueChanged: normalizedValue];
+ }
+ else if ([self findStick: &stick povNumber: &povNumber element: &element
+ withCookie: cookie])
+ {
+ int povValue = [self povValue: value forElement: element];
+ [self ddhidJoystick: self stick: stick
+ povNumber: povNumber valueChanged: povValue];
+ }
+ else
+ {
+ unsigned i = 0;
+ for (i = 0; i < [[self buttonElements] count]; i++)
+ {
+ if (cookie == [[[self buttonElements] objectAtIndex: i] cookie])
+ break;
+ }
+
+ if (value == 1)
+ {
+ [self ddhidJoystick: self buttonDown: i];
+ }
+ else if (value == 0)
+ {
+ [self ddhidJoystick: self buttonUp: i];
+ }
+ else
+ {
+ DDHidElement * element = [self elementForCookie: [event elementCookie]];
+ NSLog(@"Element: %@, value: %d", [[element usage] usageName], [event value]);
+ }
+ }
+ }
+}
+
+- (int) normalizeValue: (int) value
+ forElement: (DDHidElement *) element;
+{
+ int normalizedUnits = DDHID_JOYSTICK_VALUE_MAX - DDHID_JOYSTICK_VALUE_MIN;
+ int elementUnits = [element maxValue] - [element minValue];
+
+ int normalizedValue = (((int64_t)(value - [element minValue]) * normalizedUnits) /
+ elementUnits) + DDHID_JOYSTICK_VALUE_MIN;
+ return normalizedValue;
+}
+
+- (int) povValue: (int) value
+ forElement: (DDHidElement *) element;
+{
+ long max = [element maxValue];
+ long min = [element minValue];
+
+ // If the value is outside the min/max range, it's probably in a
+ // centered/NULL state.
+ if ((value < min) || (value > max))
+ {
+ return -1;
+ }
+
+ // Do like DirectInput and express the hatswitch value in hundredths of a
+ // degree, clockwise from north.
+ return 36000 / (max - min + 1) * (value - min);
+}
+
+- (BOOL) findStick: (unsigned *) stick
+ element: (DDHidElement **) elementOut
+ withXAxisCookie: (IOHIDElementCookie) cookie;
+{
+ unsigned i;
+ for (i = 0; i < [mSticks count]; i++)
+ {
+ DDHidElement * element = [[mSticks objectAtIndex: i] xAxisElement];
+ if ((element != nil) && ([element cookie] == cookie))
+ {
+ *stick = i;
+ *elementOut = element;
+ return YES;
+ }
+ }
+ return NO;
+}
+
+- (BOOL) findStick: (unsigned *) stick
+ element: (DDHidElement **) elementOut
+ withYAxisCookie: (IOHIDElementCookie) cookie;
+{
+ unsigned i;
+ for (i = 0; i < [mSticks count]; i++)
+ {
+ DDHidElement * element = [[mSticks objectAtIndex: i] yAxisElement];
+ if ((element != nil) && ([element cookie] == cookie))
+ {
+ *stick = i;
+ *elementOut = element;
+ return YES;
+ }
+ }
+ return NO;
+}
+
+- (BOOL) findStick: (unsigned *) stickOut
+ otherAxis: (unsigned *) axisOut
+ element: (DDHidElement **) elementOut
+ withCookie: (IOHIDElementCookie) cookie;
+{
+ unsigned i;
+ for (i = 0; i < [mSticks count]; i++)
+ {
+ DDHidJoystickStick * stick = [mSticks objectAtIndex: i];
+ unsigned j;
+ for (j = 0; j < [stick countOfStickElements]; j++)
+ {
+ DDHidElement * element = [stick objectInStickElementsAtIndex: j];
+ if ((element != nil) && ([element cookie] == cookie))
+ {
+ *stickOut = i;
+ *axisOut = j;
+ *elementOut = element;
+ return YES;
+ }
+ }
+ }
+ return NO;
+}
+
+- (BOOL) findStick: (unsigned *) stickOut
+ povNumber: (unsigned *) povNumber
+ element: (DDHidElement **) elementOut
+ withCookie: (IOHIDElementCookie) cookie;
+{
+ unsigned i;
+ for (i = 0; i < [mSticks count]; i++)
+ {
+ DDHidJoystickStick * stick = [mSticks objectAtIndex: i];
+ unsigned j;
+ for (j = 0; j < [stick countOfPovElements]; j++)
+ {
+ DDHidElement * element = [stick objectInPovElementsAtIndex: j];
+ if ((element != nil) && ([element cookie] == cookie))
+ {
+ *stickOut = i;
+ *povNumber = j;
+ *elementOut = element;
+ return YES;
+ }
+ }
+ }
+ return NO;
+}
+
+@end
+
+@implementation DDHidJoystick (DDHidJoystickDelegate)
+
+- (void) ddhidJoystick: (DDHidJoystick *) joystick
+ stick: (unsigned) stick
+ xChanged: (int) value;
+{
+ if ([mDelegate respondsToSelector: _cmd])
+ [mDelegate ddhidJoystick: joystick stick: stick xChanged: value];
+}
+
+- (void) ddhidJoystick: (DDHidJoystick *) joystick
+ stick: (unsigned) stick
+ yChanged: (int) value;
+{
+ if ([mDelegate respondsToSelector: _cmd])
+ [mDelegate ddhidJoystick: joystick stick: stick yChanged: value];
+}
+
+- (void) ddhidJoystick: (DDHidJoystick *) joystick
+ stick: (unsigned) stick
+ otherAxis: (unsigned) otherAxis
+ valueChanged: (int) value;
+{
+ if ([mDelegate respondsToSelector: _cmd])
+ [mDelegate ddhidJoystick: joystick stick: stick otherAxis: otherAxis
+ valueChanged: value];
+}
+
+- (void) ddhidJoystick: (DDHidJoystick *) joystick
+ stick: (unsigned) stick
+ povNumber: (unsigned) povNumber
+ valueChanged: (int) value;
+{
+ if ([mDelegate respondsToSelector: _cmd])
+ [mDelegate ddhidJoystick: joystick stick: stick povNumber: povNumber
+ valueChanged: value];
+}
+
+- (void) ddhidJoystick: (DDHidJoystick *) joystick
+ buttonDown: (unsigned) buttonNumber;
+{
+ if ([mDelegate respondsToSelector: _cmd])
+ [mDelegate ddhidJoystick: joystick buttonDown: buttonNumber];
+}
+
+- (void) ddhidJoystick: (DDHidJoystick *) joystick
+ buttonUp: (unsigned) buttonNumber;
+{
+ if ([mDelegate respondsToSelector: _cmd])
+ [mDelegate ddhidJoystick: joystick buttonUp: buttonNumber];
+}
+
+@end
+
+@implementation DDHidJoystickStick
+
+- (id) init
+{
+ self = [super init];
+ if (self == nil)
+ return nil;
+
+ mXAxisElement = nil;
+ mYAxisElement = nil;
+ mStickElements = [[NSMutableArray alloc] init];
+ mPovElements = [[NSMutableArray alloc] init];
+
+ return self;
+}
+
+//===========================================================
+// dealloc
+//===========================================================
+- (void) dealloc
+{
+ [mXAxisElement release];
+ [mYAxisElement release];
+ [mStickElements release];
+ [mPovElements release];
+
+ mXAxisElement = nil;
+ mYAxisElement = nil;
+ mStickElements = nil;
+ mPovElements = nil;
+ [super dealloc];
+}
+
+- (BOOL) addElement: (DDHidElement *) element;
+{
+ DDHidUsage * usage = [element usage];
+ if ([usage usagePage] != kHIDPage_GenericDesktop)
+ return NO;
+
+ BOOL elementAdded = YES;
+ switch ([usage usageId])
+ {
+ case kHIDUsage_GD_X:
+ if (mXAxisElement == nil)
+ mXAxisElement = [element retain];
+ else
+ [mStickElements addObject: element];
+ break;
+
+ case kHIDUsage_GD_Y:
+ if (mYAxisElement == nil)
+ mYAxisElement = [element retain];
+ else
+ [mStickElements addObject: element];
+ break;
+
+ case kHIDUsage_GD_Z:
+ case kHIDUsage_GD_Rx:
+ case kHIDUsage_GD_Ry:
+ case kHIDUsage_GD_Rz:
+ [mStickElements addObject: element];
+ break;
+
+ case kHIDUsage_GD_Hatswitch:
+ [mPovElements addObject: element];
+ break;
+
+ default:
+ elementAdded = NO;
+
+ }
+
+ return elementAdded;
+}
+
+- (NSArray *) allElements;
+{
+ NSMutableArray * elements = [NSMutableArray array];
+ if (mXAxisElement != nil)
+ [elements addObject: mXAxisElement];
+ if (mYAxisElement != nil)
+ [elements addObject: mYAxisElement];
+ [elements addObjectsFromArray: mStickElements];
+ [elements addObjectsFromArray: mPovElements];
+ return elements;
+}
+
+- (DDHidElement *) xAxisElement;
+{
+ return mXAxisElement;
+}
+
+- (DDHidElement *) yAxisElement;
+{
+ return mYAxisElement;
+}
+
+#pragma mark -
+#pragma mark mStickElements - indexed accessors
+
+- (unsigned int) countOfStickElements
+{
+ return [mStickElements count];
+}
+
+- (DDHidElement *) objectInStickElementsAtIndex: (unsigned int)index
+{
+ return [mStickElements objectAtIndex: index];
+}
+
+#pragma mark -
+#pragma mark PovElements - indexed accessors
+
+- (unsigned int) countOfPovElements;
+{
+ return [mPovElements count];
+}
+
+- (DDHidElement *) objectInPovElementsAtIndex: (unsigned int)index;
+{
+ return [mPovElements objectAtIndex: index];
+}
+
+- (NSString *) description;
+{
+ return [mStickElements description];
+}
+
+@end
diff --git a/lib/DDHidKeyboard.h b/lib/DDHidKeyboard.h
new file mode 100644
index 0000000..fd9200d
--- /dev/null
+++ b/lib/DDHidKeyboard.h
@@ -0,0 +1,68 @@
+/*
+ * Copyright (c) 2007 Dave Dribin
+ *
+ * Permission is hereby granted, free of charge, to any person
+ * obtaining a copy of this software and associated documentation
+ * files (the "Software"), to deal in the Software without
+ * restriction, including without limitation the rights to use, copy,
+ * modify, merge, publish, distribute, sublicense, and/or sell copies
+ * of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be
+ * included in all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+ * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
+ * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
+ * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
+ * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+ * SOFTWARE.
+ */
+
+#import <Cocoa/Cocoa.h>
+#import "DDHidDevice.h"
+
+@class DDHidElement;
+@class DDHidQueue;
+
+@interface DDHidKeyboard : DDHidDevice
+{
+ NSMutableArray * mKeyElements;
+
+ id mDelegate;
+}
+
++ (NSArray *) allKeyboards;
+
+- (id) initWithDevice: (io_object_t) device error: (NSError **) error_;
+
+#pragma mark -
+#pragma mark Keyboards Elements
+
+- (NSArray *) keyElements;
+
+- (unsigned) numberOfKeys;
+
+- (void) addElementsToQueue: (DDHidQueue *) queue;
+
+#pragma mark -
+#pragma mark Asynchronous Notification
+
+- (void) setDelegate: (id) delegate;
+
+- (void) addElementsToDefaultQueue;
+
+@end
+
+@interface NSObject (DDHidKeyboardDelegate)
+
+- (void) ddhidKeyboard: (DDHidKeyboard *) keyboard
+ keyDown: (unsigned) usageId;
+
+- (void) ddhidKeyboard: (DDHidKeyboard *) keyboard
+ keyUp: (unsigned) usageId;
+
+@end
diff --git a/lib/DDHidKeyboard.m b/lib/DDHidKeyboard.m
new file mode 100644
index 0000000..7e37264
--- /dev/null
+++ b/lib/DDHidKeyboard.m
@@ -0,0 +1,173 @@
+/*
+ * Copyright (c) 2007 Dave Dribin
+ *
+ * Permission is hereby granted, free of charge, to any person
+ * obtaining a copy of this software and associated documentation
+ * files (the "Software"), to deal in the Software without
+ * restriction, including without limitation the rights to use, copy,
+ * modify, merge, publish, distribute, sublicense, and/or sell copies
+ * of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be
+ * included in all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+ * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
+ * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
+ * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
+ * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+ * SOFTWARE.
+ */
+
+#import "DDHidKeyboard.h"
+#import "DDHidElement.h"
+#import "DDHidUsage.h"
+#import "DDHidQueue.h"
+#import "DDHidEvent.h"
+#include <IOKit/hid/IOHIDUsageTables.h>
+
+@interface DDHidKeyboard (DDHidKeyboardDelegate)
+
+- (void) ddhidKeyboard: (DDHidKeyboard *) keyboard
+ keyDown: (unsigned) usageId;
+
+- (void) ddhidKeyboard: (DDHidKeyboard *) keyboard
+ keyUp: (unsigned) usageId;
+
+@end
+
+@interface DDHidKeyboard (Private)
+
+- (void) initKeyboardElements: (NSArray *) elements;
+- (void) ddhidQueueHasEvents: (DDHidQueue *) hidQueue;
+
+@end
+
+@implementation DDHidKeyboard
+
++ (NSArray *) allKeyboards;
+{
+ return
+ [DDHidDevice allDevicesMatchingUsagePage: kHIDPage_GenericDesktop
+ usageId: kHIDUsage_GD_Keyboard
+ withClass: self
+ skipZeroLocations: YES];
+}
+
+- (id) initWithDevice: (io_object_t) device error: (NSError **) error_;
+{
+ self = [super initWithDevice: device error: error_];
+ if (self == nil)
+ return nil;
+
+ mKeyElements = [[NSMutableArray alloc] init];
+ [self initKeyboardElements: [self elements]];
+
+ return self;
+}
+
+//===========================================================
+// dealloc
+//===========================================================
+- (void) dealloc
+{
+ [mKeyElements release];
+
+ mKeyElements = nil;
+ [super dealloc];
+}
+
+#pragma mark -
+#pragma mark Keyboards Elements
+
+- (NSArray *) keyElements;
+{
+ return mKeyElements;
+}
+
+- (unsigned) numberOfKeys;
+{
+ return [mKeyElements count];
+}
+
+- (void) addElementsToQueue: (DDHidQueue *) queue;
+{
+ [queue addElements: mKeyElements];
+}
+
+#pragma mark -
+#pragma mark Asynchronous Notification
+
+- (void) setDelegate: (id) delegate;
+{
+ mDelegate = delegate;
+}
+
+- (void) addElementsToDefaultQueue;
+{
+ [self addElementsToQueue: mDefaultQueue];
+}
+
+@end
+
+@implementation DDHidKeyboard (DDHidKeyboardDelegate)
+
+- (void) ddhidKeyboard: (DDHidKeyboard *) keyboard
+ keyDown: (unsigned) usageId;
+{
+ if ([mDelegate respondsToSelector: _cmd])
+ [mDelegate ddhidKeyboard: keyboard keyDown: usageId];
+}
+
+- (void) ddhidKeyboard: (DDHidKeyboard *) keyboard
+ keyUp: (unsigned) usageId;
+{
+ if ([mDelegate respondsToSelector: _cmd])
+ [mDelegate ddhidKeyboard: keyboard keyUp: usageId];
+}
+
+@end
+
+@implementation DDHidKeyboard (Private)
+
+- (void) initKeyboardElements: (NSArray *) elements;
+{
+ NSEnumerator * e = [elements objectEnumerator];
+ DDHidElement * element;
+ while (element = [e nextObject])
+ {
+ unsigned usagePage = [[element usage] usagePage];
+ unsigned usageId = [[element usage] usageId];
+ if (usagePage == kHIDPage_KeyboardOrKeypad)
+ {
+ if ((usageId >= 0x04) && (usageId <= 0xA4) ||
+ (usageId >= 0xE0) && (usageId <= 0xE7))
+ {
+ [mKeyElements addObject: element];
+ }
+ }
+ NSArray * subElements = [element elements];
+ if (subElements != nil)
+ [self initKeyboardElements: subElements];
+ }
+}
+
+- (void) ddhidQueueHasEvents: (DDHidQueue *) hidQueue;
+{
+ DDHidEvent * event;
+ while (event = [hidQueue nextEvent])
+ {
+ DDHidElement * element = [self elementForCookie: [event elementCookie]];
+ unsigned usageId = [[element usage] usageId];
+ SInt32 value = [event value];
+ if (value == 1)
+ [self ddhidKeyboard: self keyDown: usageId];
+ else
+ [self ddhidKeyboard: self keyUp: usageId];
+ }
+}
+
+@end
diff --git a/lib/DDHidKeyboardBarcodeScanner.h b/lib/DDHidKeyboardBarcodeScanner.h
new file mode 100644
index 0000000..8504343
--- /dev/null
+++ b/lib/DDHidKeyboardBarcodeScanner.h
@@ -0,0 +1,74 @@
+/*
+ * Copyright (c) 2007 Dave Dribin, Lucas Newman
+ *
+ * Permission is hereby granted, free of charge, to any person
+ * obtaining a copy of this software and associated documentation
+ * files (the "Software"), to deal in the Software without
+ * restriction, including without limitation the rights to use, copy,
+ * modify, merge, publish, distribute, sublicense, and/or sell copies
+ * of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be
+ * included in all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+ * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
+ * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
+ * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
+ * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+ * SOFTWARE.
+ */
+
+#import <Cocoa/Cocoa.h>
+#import "DDHidDevice.h"
+
+@class DDHidElement;
+@class DDHidQueue;
+
+@interface DDHidKeyboardBarcodeScanner : DDHidDevice
+{
+ NSMutableArray * mKeyElements;
+
+ NSMutableString * mAccumulatedDigits;
+ NSTimer *mBarcodeInputTimer;
+ BOOL mIsLikelyKeyboardBarcodeScanner;
+
+ id mDelegate;
+}
+
++ (NSArray *) allPossibleKeyboardBarcodeScanners;
+
+- (id) initWithDevice: (io_object_t) device error: (NSError **) error_;
+
+#pragma mark -
+#pragma mark Keyboard Elements
+
+- (NSArray *) keyElements;
+
+- (unsigned) numberOfKeys;
+
+- (void) addElementsToQueue: (DDHidQueue *) queue;
+
+#pragma mark -
+#pragma mark Asynchronous Notification
+
+- (void) setDelegate: (id) delegate;
+
+- (void) addElementsToDefaultQueue;
+
+#pragma mark -
+#pragma mark Properties
+
+- (BOOL) isLikelyKeyboardBarcodeScanner;
+
+@end
+
+@interface NSObject (DDHidKeyboardBarcodeScannerDelegate)
+
+- (void) ddhidKeyboardBarcodeScanner: (DDHidKeyboardBarcodeScanner *) keyboardBarcodeScanner
+ gotBarcode: (NSString *) barcode;
+
+@end
diff --git a/lib/DDHidKeyboardBarcodeScanner.m b/lib/DDHidKeyboardBarcodeScanner.m
new file mode 100644
index 0000000..8705cf3
--- /dev/null
+++ b/lib/DDHidKeyboardBarcodeScanner.m
@@ -0,0 +1,222 @@
+/*
+ * Copyright (c) 2007 Dave Dribin, Lucas Newman
+ *
+ * Permission is hereby granted, free of charge, to any person
+ * obtaining a copy of this software and associated documentation
+ * files (the "Software"), to deal in the Software without
+ * restriction, including without limitation the rights to use, copy,
+ * modify, merge, publish, distribute, sublicense, and/or sell copies
+ * of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be
+ * included in all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+ * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
+ * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
+ * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
+ * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+ * SOFTWARE.
+ */
+
+#import "DDHidKeyboardBarcodeScanner.h"
+#import "DDHidElement.h"
+#import "DDHidUsage.h"
+#import "DDHidQueue.h"
+#import "DDHidEvent.h"
+#include <IOKit/hid/IOHIDUsageTables.h>
+
+@interface DDHidKeyboardBarcodeScanner (DDHidKeyboardBarcodeDelegate)
+
+- (void) ddhidKeyboardBarcodeScanner: (DDHidKeyboardBarcodeScanner *) keyboardBarcodeScanner
+ gotBarcode: (NSString *) barcode;
+
+@end
+
+@interface DDHidKeyboardBarcodeScanner (Private)
+
+- (void) initKeyboardElements: (NSArray *) elements;
+- (void) ddhidQueueHasEvents: (DDHidQueue *) hidQueue;
+- (void) processBarcodeDigit: (unsigned) usageId;
+- (void) clearAccumulatedInput;
+- (void) invalidateBarcodeInputTimer;
+
+@end
+
+@implementation DDHidKeyboardBarcodeScanner
+
++ (NSArray *) allPossibleKeyboardBarcodeScanners;
+{
+ return [DDHidDevice allDevicesMatchingUsagePage: kHIDPage_GenericDesktop
+ usageId: kHIDUsage_GD_Keyboard
+ withClass: self
+ skipZeroLocations: YES];
+}
+
+- (id) initWithDevice: (io_object_t) device error: (NSError **) error_;
+{
+ self = [super initWithDevice: device error: error_];
+ if (self == nil)
+ return nil;
+
+ mKeyElements = [[NSMutableArray alloc] init];
+ mAccumulatedDigits = [[NSMutableString alloc] init];
+ mBarcodeInputTimer = nil;
+
+ if ([[self productName] rangeOfString:@"Apple"].location != NSNotFound || [[self productName] rangeOfString:@"Internal"].location != NSNotFound)
+ mIsLikelyKeyboardBarcodeScanner = NO;
+ else
+ mIsLikelyKeyboardBarcodeScanner = YES; // if we see invalid barcodes, we can change our mind
+
+ [self initKeyboardElements: [self elements]];
+
+ return self;
+}
+
+//===========================================================
+// dealloc
+//===========================================================
+- (void) dealloc
+{
+ [self invalidateBarcodeInputTimer];
+ [mKeyElements release];
+ [mAccumulatedDigits release];
+
+ mKeyElements = nil;
+ mAccumulatedDigits = nil;
+ [super dealloc];
+}
+
+#pragma mark -
+#pragma mark Keyboard Elements
+
+- (NSArray *) keyElements;
+{
+ return mKeyElements;
+}
+
+- (unsigned) numberOfKeys;
+{
+ return [mKeyElements count];
+}
+
+- (void) addElementsToQueue: (DDHidQueue *) queue;
+{
+ [queue addElements: mKeyElements];
+}
+
+#pragma mark -
+#pragma mark Asynchronous Notification
+
+- (void) setDelegate: (id) delegate;
+{
+ mDelegate = delegate;
+}
+
+- (void) addElementsToDefaultQueue;
+{
+ [self addElementsToQueue: mDefaultQueue];
+}
+
+#pragma mark -
+#pragma mark Properties
+
+- (BOOL) isLikelyKeyboardBarcodeScanner;
+{
+ return mIsLikelyKeyboardBarcodeScanner;
+}
+
+@end
+
+@implementation DDHidKeyboardBarcodeScanner (DDHidKeyboardDelegate)
+
+- (void) ddhidKeyboardBarcodeScanner: (DDHidKeyboardBarcodeScanner *) keyboardBarcodeScanner
+ gotBarcode: (NSString *) barcode;
+{
+ if ([mDelegate respondsToSelector: _cmd])
+ [mDelegate ddhidKeyboardBarcodeScanner: keyboardBarcodeScanner gotBarcode: barcode];
+}
+
+@end
+
+@implementation DDHidKeyboardBarcodeScanner (Private)
+
+- (void) initKeyboardElements: (NSArray *) elements;
+{
+ NSEnumerator * e = [elements objectEnumerator];
+ DDHidElement * element;
+ while ((element = [e nextObject]))
+ {
+ unsigned usagePage = [[element usage] usagePage];
+ unsigned usageId = [[element usage] usageId];
+ if (usagePage == kHIDPage_KeyboardOrKeypad)
+ {
+ if ((usageId >= kHIDUsage_KeyboardA) && (usageId <= kHIDUsage_Keyboard0))
+ {
+ [mKeyElements addObject: element];
+ }
+ }
+ NSArray * subElements = [element elements];
+ if (subElements != nil)
+ [self initKeyboardElements: subElements];
+ }
+}
+
+- (void) ddhidQueueHasEvents: (DDHidQueue *) hidQueue;
+{
+ DDHidEvent * event;
+ while ((event = [hidQueue nextEvent]))
+ {
+ DDHidElement * element = [self elementForCookie: [event elementCookie]];
+ unsigned usageId = [[element usage] usageId];
+ SInt32 value = [event value];
+ if (value == 1) // key down
+ [self processBarcodeDigit: usageId];
+ }
+}
+
+#define UPC_A_BARCODE_LENGTH (12)
+#define BARCODE_INPUT_TIMEOUT (0.5)
+
+- (void) processBarcodeDigit: (unsigned) usageId;
+{
+ if (usageId <= kHIDUsage_KeyboardZ || usageId >= kHIDUsage_KeyboardCapsLock) { // an alphabetic key was pressed => probably not a barcode scanner
+ [self willChangeValueForKey:@"isLikelyKeyboardBarcodeScanner"];
+ mIsLikelyKeyboardBarcodeScanner = NO;
+ [self didChangeValueForKey:@"isLikelyKeyboardBarcodeScanner"];
+
+ [self clearAccumulatedInput];
+ return;
+ }
+
+ if (!mBarcodeInputTimer) // schedule a timer to make sure we get the rest of the digits in a timely manner
+ mBarcodeInputTimer = [[NSTimer scheduledTimerWithTimeInterval:BARCODE_INPUT_TIMEOUT target:self selector:@selector(fireBarcodeInputTimeout:) userInfo:nil repeats:NO] retain];
+
+ [mAccumulatedDigits appendString:[NSString stringWithFormat:@"%d", (usageId + 1) % 10]];
+}
+
+- (void) fireBarcodeInputTimeout: (NSTimer *) timer;
+{
+ if ([mAccumulatedDigits length] >= UPC_A_BARCODE_LENGTH)
+ [self ddhidKeyboardBarcodeScanner: self gotBarcode: [[mAccumulatedDigits copy] autorelease]];
+ [self clearAccumulatedInput];
+}
+
+- (void) clearAccumulatedInput;
+{
+ [mAccumulatedDigits deleteCharactersInRange:NSMakeRange(0, [mAccumulatedDigits length])];
+
+ [self invalidateBarcodeInputTimer];
+}
+
+- (void) invalidateBarcodeInputTimer;
+{
+ [mBarcodeInputTimer invalidate];
+ [mBarcodeInputTimer release];
+ mBarcodeInputTimer = nil;
+}
+
+@end
diff --git a/lib/DDHidLib.h b/lib/DDHidLib.h
new file mode 100644
index 0000000..20d8681
--- /dev/null
+++ b/lib/DDHidLib.h
@@ -0,0 +1,36 @@
+/*
+ * Copyright (c) 2007 Dave Dribin
+ *
+ * Permission is hereby granted, free of charge, to any person
+ * obtaining a copy of this software and associated documentation
+ * files (the "Software"), to deal in the Software without
+ * restriction, including without limitation the rights to use, copy,
+ * modify, merge, publish, distribute, sublicense, and/or sell copies
+ * of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be
+ * included in all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+ * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
+ * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
+ * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
+ * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+ * SOFTWARE.
+ */
+
+#import "DDHidDevice.h"
+#import "DDHidElement.h"
+#import "DDHidUsage.h"
+#import "DDHidQueue.h"
+#import "DDHidEvent.h"
+#import "DDHidUsageTables.h"
+#import "DDHidMouse.h"
+#import "DDHidJoystick.h"
+#import "DDHidKeyboard.h"
+#import "DDHidAppleRemote.h"
+#import "DDHidAppleMikey.h"
+#import "DDHidKeyboardBarcodeScanner.h"
diff --git a/lib/DDHidMouse.h b/lib/DDHidMouse.h
new file mode 100644
index 0000000..d396aba
--- /dev/null
+++ b/lib/DDHidMouse.h
@@ -0,0 +1,78 @@
+/*
+ * Copyright (c) 2007 Dave Dribin
+ *
+ * Permission is hereby granted, free of charge, to any person
+ * obtaining a copy of this software and associated documentation
+ * files (the "Software"), to deal in the Software without
+ * restriction, including without limitation the rights to use, copy,
+ * modify, merge, publish, distribute, sublicense, and/or sell copies
+ * of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be
+ * included in all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+ * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
+ * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
+ * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
+ * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+ * SOFTWARE.
+ */
+
+#import <Cocoa/Cocoa.h>
+#import "DDHidDevice.h"
+
+@class DDHidElement;
+@class DDHidQueue;
+
+@interface DDHidMouse : DDHidDevice
+{
+ DDHidElement * mXElement;
+ DDHidElement * mYElement;
+ DDHidElement * mWheelElement;
+ NSMutableArray * mButtonElements;
+
+ id mDelegate;
+}
+
++ (NSArray *) allMice;
+
+- (id) initWithDevice: (io_object_t) device error: (NSError **) error_;
+
+#pragma mark -
+#pragma mark Mouse Elements
+
+- (DDHidElement *) xElement;
+
+- (DDHidElement *) yElement;
+
+- (DDHidElement *) wheelElement;
+
+- (NSArray *) buttonElements;
+
+- (unsigned) numberOfButtons;
+
+- (void) addElementsToQueue: (DDHidQueue *) queue;
+
+#pragma mark -
+#pragma mark Asynchronous Notification
+
+- (void) setDelegate: (id) delegate;
+
+- (void) addElementsToDefaultQueue;
+
+@end
+
+@interface NSObject (DDHidMouseDelegate)
+
+- (void) ddhidMouse: (DDHidMouse *) mouse xChanged: (SInt32) deltaX;
+- (void) ddhidMouse: (DDHidMouse *) mouse yChanged: (SInt32) deltaY;
+- (void) ddhidMouse: (DDHidMouse *) mouse wheelChanged: (SInt32) deltaWheel;
+- (void) ddhidMouse: (DDHidMouse *) mouse buttonDown: (unsigned) buttonNumber;
+- (void) ddhidMouse: (DDHidMouse *) mouse buttonUp: (unsigned) buttonNumber;
+
+@end
+
diff --git a/lib/DDHidMouse.m b/lib/DDHidMouse.m
new file mode 100644
index 0000000..7eeb5b3
--- /dev/null
+++ b/lib/DDHidMouse.m
@@ -0,0 +1,279 @@
+/*
+ * Copyright (c) 2007 Dave Dribin
+ *
+ * Permission is hereby granted, free of charge, to any person
+ * obtaining a copy of this software and associated documentation
+ * files (the "Software"), to deal in the Software without
+ * restriction, including without limitation the rights to use, copy,
+ * modify, merge, publish, distribute, sublicense, and/or sell copies
+ * of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be
+ * included in all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+ * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
+ * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
+ * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
+ * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+ * SOFTWARE.
+ */
+
+#import "DDHidMouse.h"
+#import "DDHidUsage.h"
+#import "DDHidElement.h"
+#import "DDHidQueue.h"
+#import "DDHidEvent.h"
+#include <IOKit/hid/IOHIDUsageTables.h>
+
+
+// Implement our own delegate methods
+
+@interface DDHidMouse (DDHidMouseDelegate)
+
+- (void) ddhidMouse: (DDHidMouse *) mouse xChanged: (SInt32) deltaX;
+- (void) ddhidMouse: (DDHidMouse *) mouse yChanged: (SInt32) deltaY;
+- (void) ddhidMouse: (DDHidMouse *) mouse wheelChanged: (SInt32) deltaWheel;
+- (void) ddhidMouse: (DDHidMouse *) mouse buttonDown: (unsigned) buttonNumber;
+- (void) ddhidMouse: (DDHidMouse *) mouse buttonUp: (unsigned) buttonNumber;
+
+@end
+
+@interface DDHidMouse (Private)
+
+- (void) initMouseElements: (NSArray *) elements;
+- (void) ddhidQueueHasEvents: (DDHidQueue *) hidQueue;
+
+@end
+
+@implementation DDHidMouse
+
++ (NSArray *) allMice;
+{
+ return
+ [DDHidDevice allDevicesMatchingUsagePage: kHIDPage_GenericDesktop
+ usageId: kHIDUsage_GD_Mouse
+ withClass: self
+ skipZeroLocations: YES];
+}
+
+- (id) initWithDevice: (io_object_t) device error: (NSError **) error_;
+{
+ self = [super initWithDevice: device error: error_];
+ if (self == nil)
+ return nil;
+
+ mXElement = mYElement = mWheelElement = nil;
+ mButtonElements = [[NSMutableArray alloc] init];
+ [self initMouseElements: [self elements]];
+
+ return self;
+}
+
+//===========================================================
+// dealloc
+//===========================================================
+- (void) dealloc
+{
+ [mXElement release];
+ [mYElement release];
+ [mWheelElement release];
+ [mButtonElements release];
+
+ mXElement = nil;
+ mYElement = nil;
+ mWheelElement = nil;
+ mButtonElements = nil;
+ [super dealloc];
+}
+
+#pragma mark -
+#pragma mark Mouse Elements
+
+//===========================================================
+// - xElement
+//===========================================================
+- (DDHidElement *) xElement
+{
+ return mXElement;
+}
+
+//===========================================================
+// - yElement
+//===========================================================
+- (DDHidElement *) yElement
+{
+ return mYElement;
+}
+
+- (DDHidElement *) wheelElement;
+{
+ return mWheelElement;
+}
+
+//===========================================================
+// - buttonElements
+//===========================================================
+- (NSArray *) buttonElements;
+{
+ return mButtonElements;
+}
+
+- (unsigned) numberOfButtons;
+{
+ return [mButtonElements count];
+}
+
+- (void) addElementsToQueue: (DDHidQueue *) queue;
+{
+ [queue addElement: mXElement];
+ [queue addElement: mYElement];
+ [queue addElement: mWheelElement];
+ [queue addElements: mButtonElements];
+}
+
+
+#pragma mark -
+#pragma mark Asynchronous Notification
+
+- (void) setDelegate: (id) delegate;
+{
+ mDelegate = delegate;
+}
+
+- (void) addElementsToDefaultQueue;
+{
+ [self addElementsToQueue: mDefaultQueue];
+}
+
+@end
+
+@implementation DDHidMouse (DDHidMouseDelegate)
+
+- (void) ddhidMouse: (DDHidMouse *) mouse xChanged: (SInt32) deltaX;
+{
+ if ([mDelegate respondsToSelector: _cmd])
+ [mDelegate ddhidMouse: mouse xChanged: deltaX];
+}
+
+- (void) ddhidMouse: (DDHidMouse *) mouse yChanged: (SInt32) deltaY;
+{
+ if ([mDelegate respondsToSelector: _cmd])
+ [mDelegate ddhidMouse: mouse yChanged: deltaY];
+}
+
+- (void) ddhidMouse: (DDHidMouse *) mouse wheelChanged: (SInt32) deltaWheel;
+{
+ if ([mDelegate respondsToSelector: _cmd])
+ [mDelegate ddhidMouse: mouse wheelChanged: deltaWheel];
+}
+
+- (void) ddhidMouse: (DDHidMouse *) mouse buttonDown: (unsigned) buttonNumber;
+{
+ if ([mDelegate respondsToSelector: _cmd])
+ [mDelegate ddhidMouse: mouse buttonDown: buttonNumber];
+}
+
+- (void) ddhidMouse: (DDHidMouse *) mouse buttonUp: (unsigned) buttonNumber;
+{
+ if ([mDelegate respondsToSelector: _cmd])
+ [mDelegate ddhidMouse: mouse buttonUp: buttonNumber];
+}
+
+@end
+
+@implementation DDHidMouse (Private)
+
+- (void) initMouseElements: (NSArray *) elements;
+{
+ NSEnumerator * e = [elements objectEnumerator];
+ DDHidElement * element;
+ while (element = [e nextObject])
+ {
+ unsigned usagePage = [[element usage] usagePage];
+ unsigned usageId = [[element usage] usageId];
+ if ((usagePage == kHIDPage_GenericDesktop) &&
+ (usageId == kHIDUsage_GD_X))
+ {
+ mXElement = [element retain];
+ }
+ else if ((usagePage == kHIDPage_GenericDesktop) &&
+ (usageId == kHIDUsage_GD_Y))
+ {
+ mYElement = [element retain];
+ }
+ else if ((usagePage == kHIDPage_GenericDesktop) &&
+ (usageId == kHIDUsage_GD_Wheel))
+ {
+ mWheelElement = [element retain];
+ }
+ else if ((usagePage == kHIDPage_Button) &&
+ (usageId > 0))
+ {
+ [mButtonElements addObject: element];
+ }
+ NSArray * subElements = [element elements];
+ if (subElements != nil)
+ [self initMouseElements: subElements];
+ }
+}
+
+- (void) ddhidQueueHasEvents: (DDHidQueue *) hidQueue;
+{
+ DDHidEvent * event;
+ while (event = [hidQueue nextEvent])
+ {
+ IOHIDElementCookie cookie = [event elementCookie];
+ SInt32 value = [event value];
+ if (cookie == [[self xElement] cookie])
+ {
+ if (value != 0)
+ {
+ [self ddhidMouse: self xChanged: value];
+ }
+ }
+ else if (cookie == [[self yElement] cookie])
+ {
+ if (value != 0)
+ {
+ [self ddhidMouse: self yChanged: value];
+ }
+ }
+ else if (cookie == [[self wheelElement] cookie])
+ {
+ if (value != 0)
+ {
+ [self ddhidMouse: self wheelChanged: value];
+ }
+ }
+ else
+ {
+ unsigned i = 0;
+ for (i = 0; i < [[self buttonElements] count]; i++)
+ {
+ if (cookie == [[[self buttonElements] objectAtIndex: i] cookie])
+ break;
+ }
+
+ if (value == 1)
+ {
+ [self ddhidMouse: self buttonDown: i];
+ }
+ else if (value == 0)
+ {
+ [self ddhidMouse: self buttonUp: i];
+ }
+ else
+ {
+ DDHidElement * element = [self elementForCookie: [event elementCookie]];
+ NSLog(@"Element: %@, value: %d", [[element usage] usageName], [event value]);
+ }
+ }
+ }
+}
+
+@end
+
diff --git a/lib/DDHidQueue.h b/lib/DDHidQueue.h
new file mode 100644
index 0000000..315d003
--- /dev/null
+++ b/lib/DDHidQueue.h
@@ -0,0 +1,70 @@
+/*
+ * Copyright (c) 2007 Dave Dribin
+ *
+ * Permission is hereby granted, free of charge, to any person
+ * obtaining a copy of this software and associated documentation
+ * files (the "Software"), to deal in the Software without
+ * restriction, including without limitation the rights to use, copy,
+ * modify, merge, publish, distribute, sublicense, and/or sell copies
+ * of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be
+ * included in all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+ * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
+ * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
+ * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
+ * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+ * SOFTWARE.
+ */
+
+#import <Cocoa/Cocoa.h>
+#include <IOKit/hid/IOHIDLib.h>
+
+@class DDHidElement;
+@class DDHidEvent;
+
+@interface DDHidQueue : NSObject
+{
+ IOHIDQueueInterface ** mQueue;
+ NSRunLoop * mRunLoop;
+ BOOL mStarted;
+
+ id mDelegate;
+ CFRunLoopSourceRef mEventSource;
+}
+
+- (id) initWithHIDQueue: (IOHIDQueueInterface **) queue
+ size: (unsigned) size;
+
+- (void) addElement: (DDHidElement *) element;
+
+- (void) addElements: (NSArray *) elements;
+
+- (void) addElements: (NSArray *) elements recursively: (BOOL) recursively;
+
+- (void) setDelegate: (id) delegate;
+
+- (void) startOnCurrentRunLoop;
+
+- (void) startOnRunLoop: (NSRunLoop *) runLoop;
+
+- (void) stop;
+
+- (BOOL) isStarted;
+
+- (BOOL) getNextEvent: (IOHIDEventStruct *) event;
+
+- (DDHidEvent *) nextEvent;
+
+@end
+
+@interface NSObject (DDHidQueueDelegate)
+
+- (void) ddhidQueueHasEvents: (DDHidQueue *) hidQueue;
+
+@end
diff --git a/lib/DDHidQueue.m b/lib/DDHidQueue.m
new file mode 100644
index 0000000..8f44698
--- /dev/null
+++ b/lib/DDHidQueue.m
@@ -0,0 +1,172 @@
+/*
+ * Copyright (c) 2007 Dave Dribin
+ *
+ * Permission is hereby granted, free of charge, to any person
+ * obtaining a copy of this software and associated documentation
+ * files (the "Software"), to deal in the Software without
+ * restriction, including without limitation the rights to use, copy,
+ * modify, merge, publish, distribute, sublicense, and/or sell copies
+ * of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be
+ * included in all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+ * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
+ * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
+ * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
+ * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+ * SOFTWARE.
+ */
+
+#import "DDHidQueue.h"
+#import "DDHidElement.h"
+#import "DDHIdEvent.h"
+#import "NSXReturnThrowError.h"
+
+static void queueCallbackFunction(void* target, IOReturn result, void* refcon,
+ void* sender);
+
+@interface DDHidQueue (Private)
+
+- (void) handleQueueCallback;
+
+@end
+
+@implementation DDHidQueue
+
+- (id) initWithHIDQueue: (IOHIDQueueInterface **) queue
+ size: (unsigned) size;
+{
+ self = [super init];
+ if (self == nil)
+ return nil;
+
+ mQueue = queue;
+ IOReturn result = (*mQueue)->create(mQueue, 0, size);
+ if (result != kIOReturnSuccess)
+ {
+ [self release];
+ return nil;
+ }
+
+ return self;
+}
+
+- (void) dealloc;
+{
+ [self stop];
+ (*mQueue)->dispose(mQueue);
+ (*mQueue)->Release(mQueue);
+ [super dealloc];
+}
+
+- (void) addElement: (DDHidElement *) element;
+{
+ IOHIDElementCookie cookie = [element cookie];
+ (*mQueue)->addElement(mQueue, cookie, 0);
+}
+
+- (void) addElements: (NSArray *) elements;
+{
+ return [self addElements: elements recursively: NO];
+}
+
+- (void) addElements: (NSArray *) elements recursively: (BOOL) recursively;
+{
+ NSEnumerator * e = [elements objectEnumerator];
+ DDHidElement * element;
+ while (element = [e nextObject])
+ {
+ [self addElement: element];
+ if (recursively)
+ [self addElements: [element elements]];
+ }
+}
+
+- (void) setDelegate: (id) delegate;
+{
+ mDelegate = delegate;
+}
+
+- (void) startOnCurrentRunLoop;
+{
+ [self startOnRunLoop: [NSRunLoop currentRunLoop]];
+}
+
+- (void) startOnRunLoop: (NSRunLoop *) runLoop;
+{
+ if (mStarted)
+ return;
+
+ mRunLoop = [runLoop retain];
+
+ NSXThrowError((*mQueue)->createAsyncEventSource(mQueue, &mEventSource));
+ NSXThrowError((*mQueue)->setEventCallout(mQueue, queueCallbackFunction, self, NULL));
+ CFRunLoopAddSource([mRunLoop getCFRunLoop], mEventSource,
+ kCFRunLoopDefaultMode);
+ (*mQueue)->start(mQueue);
+ mStarted = YES;
+}
+
+- (void) stop;
+{
+ if (!mStarted)
+ return;
+
+ CFRunLoopRemoveSource([mRunLoop getCFRunLoop], mEventSource, kCFRunLoopDefaultMode);
+ (*mQueue)->stop(mQueue);
+ [mRunLoop release];
+ mRunLoop = nil;
+ mStarted = NO;
+}
+
+- (BOOL) isStarted;
+{
+ return mStarted;
+}
+
+- (BOOL) getNextEvent: (IOHIDEventStruct *) event;
+{
+ AbsoluteTime zeroTime = {0, 0};
+ IOReturn result = (*mQueue)->getNextEvent(mQueue, event, zeroTime, 0);
+ return (result == kIOReturnSuccess);
+}
+
+- (DDHidEvent *) nextEvent;
+{
+ AbsoluteTime zeroTime = {0, 0};
+ IOHIDEventStruct event;
+ IOReturn result = (*mQueue)->getNextEvent(mQueue, &event, zeroTime, 0);
+ if (result != kIOReturnSuccess)
+ return nil;
+ else
+ return [DDHidEvent eventWithIOHIDEvent: &event];
+}
+
+@end
+
+@implementation DDHidQueue (Private)
+
+- (void) handleQueueCallback;
+{
+ if ([mDelegate respondsToSelector: @selector(ddhidQueueHasEvents:)])
+ {
+ [mDelegate ddhidQueueHasEvents: self];
+ }
+}
+
+@end
+
+static void queueCallbackFunction(void* target, IOReturn result, void* refcon,
+ void* sender)
+{
+ NSAutoreleasePool * pool = [[NSAutoreleasePool alloc] init];
+ DDHidQueue * queue = (DDHidQueue *) target;
+ [queue handleQueueCallback];
+ [pool release];
+
+}
diff --git a/lib/DDHidStandardUsages.plist b/lib/DDHidStandardUsages.plist
new file mode 100644
index 0000000..84eb663
--- /dev/null
+++ b/lib/DDHidStandardUsages.plist
@@ -0,0 +1,2327 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!DOCTYPE plist PUBLIC "-//Apple Computer//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
+<plist version="1.0">
+ <dict>
+ <key>1</key>
+ <dict>
+ <key>description</key>
+ <string>Generic Desktop</string>
+ <key>ranges</key>
+ <dict>
+ </dict>
+ <key>usages</key>
+ <dict>
+ <key>0</key>
+ <string>Undefined</string>
+ <key>1</key>
+ <string>Pointer</string>
+ <key>2</key>
+ <string>Mouse</string>
+ <key>3</key>
+ <string>Reserved</string>
+ <key>4</key>
+ <string>Joystick</string>
+ <key>5</key>
+ <string>Game Pad</string>
+ <key>6</key>
+ <string>Keyboard</string>
+ <key>7</key>
+ <string>Keypad</string>
+ <key>8</key>
+ <string>Multi-axis Controller</string>
+ <key>48</key>
+ <string>X</string>
+ <key>49</key>
+ <string>Y</string>
+ <key>50</key>
+ <string>Z</string>
+ <key>51</key>
+ <string>Rx</string>
+ <key>52</key>
+ <string>Ry</string>
+ <key>53</key>
+ <string>Rz</string>
+ <key>54</key>
+ <string>Slider</string>
+ <key>55</key>
+ <string>Dial</string>
+ <key>56</key>
+ <string>Wheel</string>
+ <key>57</key>
+ <string>Hat Switch</string>
+ <key>58</key>
+ <string>Counted Buffer</string>
+ <key>59</key>
+ <string>Byte Count</string>
+ <key>60</key>
+ <string>Motion Wakeup</string>
+ <key>64</key>
+ <string>Vx</string>
+ <key>65</key>
+ <string>Vy</string>
+ <key>66</key>
+ <string>Vz</string>
+ <key>67</key>
+ <string>Vbrx</string>
+ <key>68</key>
+ <string>Vbry</string>
+ <key>69</key>
+ <string>Vbrx</string>
+ <key>70</key>
+ <string>Vno</string>
+ <key>128</key>
+ <string>System Control</string>
+ <key>129</key>
+ <string>System Power Down</string>
+ <key>130</key>
+ <string>System Sleep</string>
+ <key>131</key>
+ <string>System Wake Up</string>
+ <key>132</key>
+ <string>System Context Menu</string>
+ <key>133</key>
+ <string>System Main Menu</string>
+ <key>134</key>
+ <string>System App Menu</string>
+ <key>135</key>
+ <string>System Menu Help</string>
+ <key>136</key>
+ <string>System Menu Exit</string>
+ <key>137</key>
+ <string>System Menu Select</string>
+ <key>138</key>
+ <string>System Menu Right</string>
+ <key>139</key>
+ <string>System Menu Left</string>
+ <key>140</key>
+ <string>System Menu Up</string>
+ <key>141</key>
+ <string>System Menu Down</string>
+ <key>144</key>
+ <string>D-pad Up</string>
+ <key>145</key>
+ <string>D-pad Down</string>
+ <key>146</key>
+ <string>D-pad Right</string>
+ <key>147</key>
+ <string>D-pad Left</string>
+ </dict>
+ </dict>
+ <key>2</key>
+ <dict>
+ <key>description</key>
+ <string>Simulation Controls</string>
+ <key>ranges</key>
+ <dict>
+ </dict>
+ <key>usages</key>
+ <dict>
+ <key>0</key>
+ <string>Undefined</string>
+ <key>1</key>
+ <string>Flight Simulation Device</string>
+ <key>2</key>
+ <string>Automobile Simulation Device</string>
+ <key>3</key>
+ <string>Tank Simulation Device</string>
+ <key>4</key>
+ <string>Spaceship Simulation Device</string>
+ <key>5</key>
+ <string>Submarine Simulation Device</string>
+ <key>6</key>
+ <string>Sailing Simulation Device</string>
+ <key>7</key>
+ <string>Motorcycle Simulation Device</string>
+ <key>8</key>
+ <string>Sports Simulation Device</string>
+ <key>9</key>
+ <string>Airplane Simulation Device</string>
+ <key>10</key>
+ <string>Helicopter Simulation Device</string>
+ <key>11</key>
+ <string>Magic Carpet Simulation Device</string>
+ <key>12</key>
+ <string>Bicycle</string>
+ <key>32</key>
+ <string>Flight Control Stick</string>
+ <key>33</key>
+ <string>Flight Stick</string>
+ <key>34</key>
+ <string>Cyclic Control</string>
+ <key>35</key>
+ <string>Cyclic Trim</string>
+ <key>36</key>
+ <string>Flight Yoke</string>
+ <key>37</key>
+ <string>Track Control</string>
+ <key>38</key>
+ <string>Driving Control</string>
+ <key>176</key>
+ <string>Aileron</string>
+ <key>177</key>
+ <string>Aileron Trim</string>
+ <key>178</key>
+ <string>Anti-Torque Control</string>
+ <key>179</key>
+ <string>Auto-pilot Enable</string>
+ <key>180</key>
+ <string>Chaff Release</string>
+ <key>181</key>
+ <string>Collective Control</string>
+ <key>182</key>
+ <string>Dive Brake</string>
+ <key>183</key>
+ <string>Electronic Counter Measures</string>
+ <key>184</key>
+ <string>Elevator</string>
+ <key>185</key>
+ <string>Elevator Trim</string>
+ <key>186</key>
+ <string>Rudder</string>
+ <key>187</key>
+ <string>Throttle</string>
+ <key>188</key>
+ <string>Flight Communication</string>
+ <key>189</key>
+ <string>Flare Release</string>
+ <key>190</key>
+ <string>Landing Gear</string>
+ <key>191</key>
+ <string>Toe Brake</string>
+ <key>192</key>
+ <string>Trigger</string>
+ <key>193</key>
+ <string>Weapons Arm</string>
+ <key>194</key>
+ <string>Weapons Select</string>
+ <key>195</key>
+ <string>Wing Flaps</string>
+ <key>196</key>
+ <string>Accelerator</string>
+ <key>197</key>
+ <string>Brake</string>
+ <key>198</key>
+ <string>Clutch</string>
+ <key>199</key>
+ <string>Shifter</string>
+ <key>200</key>
+ <string>Steering</string>
+ <key>201</key>
+ <string>Turret Direction</string>
+ <key>202</key>
+ <string>Barrel Elevation</string>
+ <key>203</key>
+ <string>Dive Plane</string>
+ <key>204</key>
+ <string>Ballast</string>
+ <key>205</key>
+ <string>Bicycle Crank</string>
+ <key>206</key>
+ <string>Handle Bars</string>
+ <key>207</key>
+ <string>Front Brake</string>
+ <key>208</key>
+ <string>Rear Brake</string>
+ </dict>
+ </dict>
+ <key>3</key>
+ <dict>
+ <key>description</key>
+ <string>VR Controls</string>
+ <key>ranges</key>
+ <dict>
+ </dict>
+ <key>usages</key>
+ <dict>
+ <key>0</key>
+ <string>Unidentified</string>
+ <key>1</key>
+ <string>Belt</string>
+ <key>2</key>
+ <string>Body Suit</string>
+ <key>3</key>
+ <string>Flexor</string>
+ <key>4</key>
+ <string>Glove</string>
+ <key>5</key>
+ <string>Head Tracker</string>
+ <key>6</key>
+ <string>Head Mounted Display</string>
+ <key>7</key>
+ <string>Hand Tracker</string>
+ <key>8</key>
+ <string>Oculometer</string>
+ <key>9</key>
+ <string>Vest</string>
+ <key>10</key>
+ <string>Animatronic Device</string>
+ <key>32</key>
+ <string>Stereo Enable</string>
+ <key>33</key>
+ <string>Display Enable</string>
+ </dict>
+ </dict>
+ <key>4</key>
+ <dict>
+ <key>description</key>
+ <string>Sports Controls</string>
+ <key>ranges</key>
+ <dict>
+ </dict>
+ <key>usages</key>
+ <dict>
+ <key>0</key>
+ <string>Unidentified</string>
+ <key>1</key>
+ <string>Baseball Bat</string>
+ <key>2</key>
+ <string>Golf Club</string>
+ <key>3</key>
+ <string>Rowing Machine</string>
+ <key>4</key>
+ <string>Treadmill</string>
+ <key>48</key>
+ <string>Oar</string>
+ <key>49</key>
+ <string>Slope</string>
+ <key>50</key>
+ <string>Rate</string>
+ <key>51</key>
+ <string>Stick Speed</string>
+ <key>52</key>
+ <string>Stick Face Angle</string>
+ <key>53</key>
+ <string>Stick Heel/Toe</string>
+ <key>54</key>
+ <string>Stick Follow Through</string>
+ <key>55</key>
+ <string>Stick Tempo</string>
+ <key>56</key>
+ <string>Stick Type</string>
+ <key>57</key>
+ <string>Stick Height</string>
+ <key>80</key>
+ <string>Putter</string>
+ <key>81</key>
+ <string>1 Iron</string>
+ <key>82</key>
+ <string>2 Iron</string>
+ <key>83</key>
+ <string>3 Iron</string>
+ <key>84</key>
+ <string>4 Iron</string>
+ <key>85</key>
+ <string>5 Iron</string>
+ <key>86</key>
+ <string>6 Iron</string>
+ <key>87</key>
+ <string>7 Iron</string>
+ <key>88</key>
+ <string>8 Iron</string>
+ <key>89</key>
+ <string>9 Iron</string>
+ <key>90</key>
+ <string>10 Iron</string>
+ <key>91</key>
+ <string>11 Iron</string>
+ <key>92</key>
+ <string>Sand Wedge</string>
+ <key>93</key>
+ <string>Loft Wedge</string>
+ <key>94</key>
+ <string>Power Wedge</string>
+ <key>95</key>
+ <string>1 Wood</string>
+ <key>96</key>
+ <string>3 Wood</string>
+ <key>97</key>
+ <string>5 Wood</string>
+ <key>98</key>
+ <string>7 Wood</string>
+ <key>99</key>
+ <string>9 Wood</string>
+ </dict>
+ </dict>
+ <key>5</key>
+ <dict>
+ <key>description</key>
+ <string>Game Controls</string>
+ <key>ranges</key>
+ <dict>
+ </dict>
+ <key>usages</key>
+ <dict>
+ <key>0</key>
+ <string>Undefined</string>
+ <key>1</key>
+ <string>3D Game Controller</string>
+ <key>2</key>
+ <string>Pinball Device</string>
+ <key>3</key>
+ <string>Gun Device</string>
+ <key>32</key>
+ <string>Point of View</string>
+ <key>33</key>
+ <string>Turn Right/Left</string>
+ <key>34</key>
+ <string>Pitch Right/Left</string>
+ <key>35</key>
+ <string>Roll Forward/Backward</string>
+ <key>36</key>
+ <string>Move Right/Left</string>
+ <key>37</key>
+ <string>Move Forward/Backward</string>
+ <key>38</key>
+ <string>Move Up/Down</string>
+ <key>39</key>
+ <string>Lean Right/Left</string>
+ <key>40</key>
+ <string>Lean Forward/Backward</string>
+ <key>41</key>
+ <string>Height of POV</string>
+ <key>42</key>
+ <string>Flipper</string>
+ <key>43</key>
+ <string>Secondary Flipper</string>
+ <key>44</key>
+ <string>Bump</string>
+ <key>45</key>
+ <string>New Game</string>
+ <key>46</key>
+ <string>Shoot Ball</string>
+ <key>47</key>
+ <string>Player</string>
+ <key>48</key>
+ <string>Gun Bolt</string>
+ <key>49</key>
+ <string>Gun Clip</string>
+ <key>50</key>
+ <string>Gun Selector</string>
+ <key>51</key>
+ <string>Gun Single Shot</string>
+ <key>52</key>
+ <string>Gun Burst</string>
+ <key>53</key>
+ <string>Gun Automatic</string>
+ <key>54</key>
+ <string>Gun Safety</string>
+ <key>55</key>
+ <string>Gamepad Fire/Jump</string>
+ <key>57</key>
+ <string>Gamepad Trigger</string>
+ </dict>
+ </dict>
+ <key>7</key>
+ <dict>
+ <key>description</key>
+ <string>Keyboard</string>
+ <key>ranges</key>
+ <dict>
+ </dict>
+ <key>usages</key>
+ <dict>
+ <key>0</key>
+ <string>No Event</string>
+ <key>1</key>
+ <string>Keyboard ErrorRollOver</string>
+ <key>2</key>
+ <string>Keyboard POSTFail</string>
+ <key>3</key>
+ <string>Keyboard ErrorUndefined</string>
+ <key>4</key>
+ <string>Keyboard a and A</string>
+ <key>5</key>
+ <string>Keyboard b and B</string>
+ <key>6</key>
+ <string>Keyboard c and C</string>
+ <key>7</key>
+ <string>Keyboard d and D</string>
+ <key>8</key>
+ <string>Keyboard e and E</string>
+ <key>9</key>
+ <string>Keyboard f and F</string>
+ <key>10</key>
+ <string>Keyboard g and G</string>
+ <key>11</key>
+ <string>Keyboard h and H</string>
+ <key>12</key>
+ <string>Keyboard i and I</string>
+ <key>13</key>
+ <string>Keyboard j and J</string>
+ <key>14</key>
+ <string>Keyboard k and K</string>
+ <key>15</key>
+ <string>Keyboard l and L</string>
+ <key>16</key>
+ <string>Keyboard m and M</string>
+ <key>17</key>
+ <string>Keyboard n and N</string>
+ <key>18</key>
+ <string>Keyboard o and O</string>
+ <key>19</key>
+ <string>Keyboard p and P</string>
+ <key>20</key>
+ <string>Keyboard q and Q</string>
+ <key>21</key>
+ <string>Keyboard r and R</string>
+ <key>22</key>
+ <string>Keyboard s and S</string>
+ <key>23</key>
+ <string>Keyboard t and T</string>
+ <key>24</key>
+ <string>Keyboard u and U</string>
+ <key>25</key>
+ <string>Keyboard v and V</string>
+ <key>26</key>
+ <string>Keyboard w and W</string>
+ <key>27</key>
+ <string>Keyboard x and X</string>
+ <key>28</key>
+ <string>Keyboard y and Y</string>
+ <key>29</key>
+ <string>Keyboard z and Z</string>
+ <key>30</key>
+ <string>Keyboard 1 and !</string>
+ <key>31</key>
+ <string>Keyboard 2 and @</string>
+ <key>32</key>
+ <string>Keyboard 3 and #</string>
+ <key>33</key>
+ <string>Keyboard 4 and $</string>
+ <key>34</key>
+ <string>Keyboard 5 and %</string>
+ <key>35</key>
+ <string>Keyboard 6 and ^</string>
+ <key>36</key>
+ <string>Keyboard 7 and &amp;</string>
+ <key>37</key>
+ <string>Keyboard 8 and *</string>
+ <key>38</key>
+ <string>Keyboard 9 and (</string>
+ <key>39</key>
+ <string>Keyboard 0 and )</string>
+ <key>40</key>
+ <string>Keyboard Return (ENTER)</string>
+ <key>41</key>
+ <string>Keyboard ESCAPE</string>
+ <key>42</key>
+ <string>Keyboard DELETE (Backspace)</string>
+ <key>43</key>
+ <string>Keyboard Tab</string>
+ <key>44</key>
+ <string>Keyboard Spacebar</string>
+ <key>45</key>
+ <string>Keyboard - and (underscore)</string>
+ <key>46</key>
+ <string>Keyboard = and +</string>
+ <key>47</key>
+ <string>Keyboard [ and {</string>
+ <key>48</key>
+ <string>Keyboard ] and }</string>
+ <key>49</key>
+ <string>Keyboard \ and |</string>
+ <key>50</key>
+ <string>Keyboard Non-US # and ~</string>
+ <key>51</key>
+ <string>Keyboard ; and :</string>
+ <key>52</key>
+ <string>Keyboard ' and "</string>
+ <key>53</key>
+ <string>Keyboard Grave Accent and Tilde</string>
+ <key>54</key>
+ <string>Keyboard, and &lt;</string>
+ <key>55</key>
+ <string>Keyboard . and &gt;</string>
+ <key>56</key>
+ <string>Keyboard / and ?</string>
+ <key>57</key>
+ <string>Keyboard Caps Lock</string>
+ <key>58</key>
+ <string>Keyboard F1</string>
+ <key>59</key>
+ <string>Keyboard F2</string>
+ <key>60</key>
+ <string>Keyboard F3</string>
+ <key>61</key>
+ <string>Keyboard F4</string>
+ <key>62</key>
+ <string>Keyboard F5</string>
+ <key>63</key>
+ <string>Keyboard F6</string>
+ <key>64</key>
+ <string>Keyboard F7</string>
+ <key>65</key>
+ <string>Keyboard F8</string>
+ <key>66</key>
+ <string>Keyboard F9</string>
+ <key>67</key>
+ <string>Keyboard F10</string>
+ <key>68</key>
+ <string>Keyboard F11</string>
+ <key>69</key>
+ <string>Keyboard F12</string>
+ <key>70</key>
+ <string>Keyboard PrintScreen</string>
+ <key>71</key>
+ <string>Keyboard Scroll Lock</string>
+ <key>72</key>
+ <string>Keyboard Pause</string>
+ <key>73</key>
+ <string>Keyboard Insert</string>
+ <key>74</key>
+ <string>Keyboard Home</string>
+ <key>75</key>
+ <string>Keyboard PageUp</string>
+ <key>76</key>
+ <string>Keyboard Delete Forward</string>
+ <key>77</key>
+ <string>Keyboard End</string>
+ <key>78</key>
+ <string>Keyboard PageDown</string>
+ <key>79</key>
+ <string>Keyboard RightArrow</string>
+ <key>80</key>
+ <string>Keyboard LeftArrow</string>
+ <key>81</key>
+ <string>Keyboard DownArrow</string>
+ <key>82</key>
+ <string>Keyboard UpArrow</string>
+ <key>83</key>
+ <string>Keypad Num Lock and Clear</string>
+ <key>84</key>
+ <string>Keypad /</string>
+ <key>85</key>
+ <string>Keypad *</string>
+ <key>86</key>
+ <string>Keypad -</string>
+ <key>87</key>
+ <string>Keypad +</string>
+ <key>88</key>
+ <string>Keypad ENTER</string>
+ <key>89</key>
+ <string>Keypad 1 and End</string>
+ <key>90</key>
+ <string>Keypad 2 and Down Arrow</string>
+ <key>91</key>
+ <string>Keypad 3 and PageDn</string>
+ <key>92</key>
+ <string>Keypad 4 and Left Arrow</string>
+ <key>93</key>
+ <string>Keypad 5</string>
+ <key>94</key>
+ <string>Keypad 6 and Right Arrow</string>
+ <key>95</key>
+ <string>Keypad 7 and Home</string>
+ <key>96</key>
+ <string>Keypad 8 and Up Arrow</string>
+ <key>97</key>
+ <string>Keypad 9 and PageUp</string>
+ <key>98</key>
+ <string>Keypad 0 and Insert</string>
+ <key>99</key>
+ <string>Keypad . and Delete</string>
+ <key>100</key>
+ <string>Keyboard Non-US \ and |</string>
+ <key>101</key>
+ <string>Keyboard Application</string>
+ <key>102</key>
+ <string>Keyboard Power</string>
+ <key>103</key>
+ <string>Keypad =</string>
+ <key>104</key>
+ <string>Keyboard F13</string>
+ <key>105</key>
+ <string>Keyboard F14</string>
+ <key>106</key>
+ <string>Keyboard F15</string>
+ <key>107</key>
+ <string>Keyboard F16</string>
+ <key>108</key>
+ <string>Keyboard F17</string>
+ <key>109</key>
+ <string>Keyboard F18</string>
+ <key>110</key>
+ <string>Keyboard F19</string>
+ <key>111</key>
+ <string>Keyboard F20</string>
+ <key>112</key>
+ <string>Keyboard F21</string>
+ <key>113</key>
+ <string>Keyboard F22</string>
+ <key>114</key>
+ <string>Keyboard F23</string>
+ <key>115</key>
+ <string>Keyboard F24</string>
+ <key>116</key>
+ <string>Keyboard Execute</string>
+ <key>117</key>
+ <string>Keyboard Help</string>
+ <key>118</key>
+ <string>Keyboard Menu</string>
+ <key>119</key>
+ <string>Keyboard Select</string>
+ <key>120</key>
+ <string>Keyboard Stop</string>
+ <key>121</key>
+ <string>Keyboard Again</string>
+ <key>122</key>
+ <string>Keyboard Undo</string>
+ <key>123</key>
+ <string>Keyboard Cut</string>
+ <key>124</key>
+ <string>Keyboard Copy</string>
+ <key>125</key>
+ <string>Keyboard Paste</string>
+ <key>126</key>
+ <string>Keyboard Find</string>
+ <key>127</key>
+ <string>Keyboard Mute</string>
+ <key>128</key>
+ <string>Keyboard Volume Up</string>
+ <key>129</key>
+ <string>Keyboard Volume Down</string>
+ <key>130</key>
+ <string>Keyboard Locking Caps Lock</string>
+ <key>131</key>
+ <string>Keyboard Locking Num Lock</string>
+ <key>132</key>
+ <string>Keyboard Locking Scroll Lock</string>
+ <key>133</key>
+ <string>Keypad Comma</string>
+ <key>134</key>
+ <string>Keypad Equal Sign</string>
+ <key>135</key>
+ <string>Keyboard International1</string>
+ <key>136</key>
+ <string>Keyboard International2</string>
+ <key>137</key>
+ <string>Keyboard International3</string>
+ <key>138</key>
+ <string>Keyboard International4</string>
+ <key>139</key>
+ <string>Keyboard International5</string>
+ <key>140</key>
+ <string>Keyboard International6</string>
+ <key>141</key>
+ <string>Keyboard International7</string>
+ <key>142</key>
+ <string>Keyboard International8</string>
+ <key>143</key>
+ <string>Keyboard International9</string>
+ <key>144</key>
+ <string>Keyboard LANG1</string>
+ <key>145</key>
+ <string>Keyboard LANG2</string>
+ <key>146</key>
+ <string>Keyboard LANG3</string>
+ <key>147</key>
+ <string>Keyboard LANG4</string>
+ <key>148</key>
+ <string>Keyboard LANG5</string>
+ <key>149</key>
+ <string>Keyboard LANG6</string>
+ <key>150</key>
+ <string>Keyboard LANG7</string>
+ <key>151</key>
+ <string>Keyboard LANG8</string>
+ <key>152</key>
+ <string>Keyboard LANG9</string>
+ <key>153</key>
+ <string>Keyboard Alternate Erase</string>
+ <key>154</key>
+ <string>Keyboard SysReq/Attention</string>
+ <key>155</key>
+ <string>Keyboard Cancel</string>
+ <key>156</key>
+ <string>Keyboard Clear</string>
+ <key>157</key>
+ <string>Keyboard Prior</string>
+ <key>158</key>
+ <string>Keyboard Return</string>
+ <key>159</key>
+ <string>Keyboard Separator</string>
+ <key>160</key>
+ <string>Keyboard Out</string>
+ <key>161</key>
+ <string>Keyboard Oper</string>
+ <key>162</key>
+ <string>Keyboard Clear/Again</string>
+ <key>163</key>
+ <string>Keyboard CrSel/Props</string>
+ <key>164</key>
+ <string>Keyboard ExSel</string>
+ <key>224</key>
+ <string>Keyboard LeftControl</string>
+ <key>225</key>
+ <string>Keyboard LeftShift</string>
+ <key>226</key>
+ <string>Keyboard LeftAlt</string>
+ <key>227</key>
+ <string>Keyboard Left GUI</string>
+ <key>228</key>
+ <string>Keyboard RightControl</string>
+ <key>229</key>
+ <string>Keyboard RightShift</string>
+ <key>230</key>
+ <string>Keyboard RightAlt</string>
+ <key>231</key>
+ <string>Keyboard Right GUI</string>
+ </dict>
+ </dict>
+ <key>8</key>
+ <dict>
+ <key>description</key>
+ <string>LEDs</string>
+ <key>ranges</key>
+ <dict>
+ <key>{76, 65535}</key>
+ <string>Reserved</string>
+ </dict>
+ <key>usages</key>
+ <dict>
+ <key>0</key>
+ <string>Undefined</string>
+ <key>1</key>
+ <string>Num Lock</string>
+ <key>2</key>
+ <string>Caps Lock</string>
+ <key>3</key>
+ <string>Scroll Lock</string>
+ <key>4</key>
+ <string>Compose</string>
+ <key>5</key>
+ <string>Kana</string>
+ <key>6</key>
+ <string>Power</string>
+ <key>7</key>
+ <string>Shift</string>
+ <key>8</key>
+ <string>Do Not Disturb</string>
+ <key>9</key>
+ <string>Mute</string>
+ <key>10</key>
+ <string>Tone Enable</string>
+ <key>11</key>
+ <string>High Cut Filter</string>
+ <key>12</key>
+ <string>Low Cut Filter</string>
+ <key>13</key>
+ <string>Equalizer Enable</string>
+ <key>14</key>
+ <string>Sound Field On</string>
+ <key>15</key>
+ <string>Surround Field On</string>
+ <key>16</key>
+ <string>Repeat</string>
+ <key>17</key>
+ <string>Stereo</string>
+ <key>18</key>
+ <string>Sampling Rate Detect</string>
+ <key>19</key>
+ <string>Spinning</string>
+ <key>20</key>
+ <string>CAV</string>
+ <key>21</key>
+ <string>CLV</string>
+ <key>22</key>
+ <string>Recording Format Detect</string>
+ <key>23</key>
+ <string>Off-Hook</string>
+ <key>24</key>
+ <string>Ring</string>
+ <key>25</key>
+ <string>Message Waiting</string>
+ <key>26</key>
+ <string>Data Mode</string>
+ <key>27</key>
+ <string>Battery Operation</string>
+ <key>28</key>
+ <string>Battery OK</string>
+ <key>29</key>
+ <string>Battery Low</string>
+ <key>30</key>
+ <string>Speaker</string>
+ <key>31</key>
+ <string>Head Set</string>
+ <key>32</key>
+ <string>Hold</string>
+ <key>33</key>
+ <string>Microphone</string>
+ <key>34</key>
+ <string>Coverage</string>
+ <key>35</key>
+ <string>Night Mode</string>
+ <key>36</key>
+ <string>Send Calls</string>
+ <key>37</key>
+ <string>Call Pickup</string>
+ <key>38</key>
+ <string>Conference</string>
+ <key>39</key>
+ <string>Stand-by</string>
+ <key>40</key>
+ <string>Camera On</string>
+ <key>41</key>
+ <string>Camera Off</string>
+ <key>42</key>
+ <string>On-Line</string>
+ <key>43</key>
+ <string>Off-Line</string>
+ <key>44</key>
+ <string>Busy</string>
+ <key>45</key>
+ <string>Ready</string>
+ <key>46</key>
+ <string>Paper-Out</string>
+ <key>47</key>
+ <string>Paper-Jam</string>
+ <key>48</key>
+ <string>Remote</string>
+ <key>49</key>
+ <string>Forward</string>
+ <key>50</key>
+ <string>Reverse</string>
+ <key>51</key>
+ <string>Stop</string>
+ <key>52</key>
+ <string>Rewind</string>
+ <key>53</key>
+ <string>Fast Forward</string>
+ <key>54</key>
+ <string>Play</string>
+ <key>55</key>
+ <string>Pause</string>
+ <key>56</key>
+ <string>Record</string>
+ <key>57</key>
+ <string>Error</string>
+ <key>58</key>
+ <string>Usage Selected Indicator</string>
+ <key>59</key>
+ <string>Usage In Use Indicator</string>
+ <key>60</key>
+ <string>Usage Multi Mode Indicator</string>
+ <key>61</key>
+ <string>Indicator On</string>
+ <key>62</key>
+ <string>Indicator Flash</string>
+ <key>63</key>
+ <string>Indicator Slow Blink</string>
+ <key>64</key>
+ <string>Indicator Fast Blink</string>
+ <key>65</key>
+ <string>Indicator Off</string>
+ <key>66</key>
+ <string>Flash On Time</string>
+ <key>67</key>
+ <string>Slow Blink On Time</string>
+ <key>68</key>
+ <string>Slow Blink Off Time</string>
+ <key>69</key>
+ <string>Fast Blink On Time</string>
+ <key>70</key>
+ <string>Fast Blink Off Time</string>
+ <key>71</key>
+ <string>Usage Indicator Color</string>
+ <key>72</key>
+ <string>Red</string>
+ <key>73</key>
+ <string>Green</string>
+ <key>74</key>
+ <string>Amber</string>
+ <key>75</key>
+ <string>Generic Indicator</string>
+ <key>76</key>
+ <string>System Suspend</string>
+ <key>77</key>
+ <string>External Power Connected</string>
+ </dict>
+ </dict>
+ <key>9</key>
+ <dict>
+ <key>default</key>
+ <string>Button %d</string>
+ <key>description</key>
+ <string>Button</string>
+ <key>ranges</key>
+ <dict>
+ </dict>
+ <key>usages</key>
+ <dict>
+ <key>0</key>
+ <string>No Button Pressed</string>
+ </dict>
+ </dict>
+ <key>10</key>
+ <dict>
+ <key>default</key>
+ <string>Instance %d</string>
+ <key>description</key>
+ <string>Ordinal</string>
+ <key>ranges</key>
+ <dict>
+ </dict>
+ <key>usages</key>
+ <dict>
+ <key>0</key>
+ <string>Unused</string>
+ </dict>
+ </dict>
+ <key>11</key>
+ <dict>
+ <key>description</key>
+ <string>Telephony</string>
+ <key>ranges</key>
+ <dict>
+ </dict>
+ <key>usages</key>
+ <dict>
+ <key>0</key>
+ <string>Unassigned</string>
+ <key>1</key>
+ <string>Phone</string>
+ <key>2</key>
+ <string>Answering Machine</string>
+ <key>3</key>
+ <string>Message Controls</string>
+ <key>4</key>
+ <string>Handset</string>
+ <key>5</key>
+ <string>Headset</string>
+ <key>6</key>
+ <string>Telephony Key Pad</string>
+ <key>7</key>
+ <string>Programmable Button</string>
+ <key>32</key>
+ <string>Hook Switch</string>
+ <key>33</key>
+ <string>Flash</string>
+ <key>34</key>
+ <string>Feature</string>
+ <key>35</key>
+ <string>Hold</string>
+ <key>36</key>
+ <string>Redial</string>
+ <key>37</key>
+ <string>Transfer</string>
+ <key>38</key>
+ <string>Drop</string>
+ <key>39</key>
+ <string>Park</string>
+ <key>40</key>
+ <string>Forward Calls</string>
+ <key>41</key>
+ <string>Alternate Function</string>
+ <key>42</key>
+ <string>Line</string>
+ <key>43</key>
+ <string>Speaker Phone</string>
+ <key>44</key>
+ <string>Conference</string>
+ <key>45</key>
+ <string>Ring Enable</string>
+ <key>46</key>
+ <string>Ring Select</string>
+ <key>47</key>
+ <string>Phone Mute</string>
+ <key>48</key>
+ <string>Caller ID</string>
+ <key>80</key>
+ <string>Speed Dial</string>
+ <key>81</key>
+ <string>Store Number</string>
+ <key>82</key>
+ <string>Recall Number</string>
+ <key>83</key>
+ <string>Phone Directory</string>
+ <key>112</key>
+ <string>Voice Mail</string>
+ <key>113</key>
+ <string>Screen Calls</string>
+ <key>114</key>
+ <string>Do Not Disturb</string>
+ <key>115</key>
+ <string>Message</string>
+ <key>116</key>
+ <string>Answer On/Off</string>
+ <key>144</key>
+ <string>Inside Dial Tone</string>
+ <key>145</key>
+ <string>Outside Dial Tone</string>
+ <key>146</key>
+ <string>Inside Ring Tone</string>
+ <key>147</key>
+ <string>Outside Ring Tone</string>
+ <key>148</key>
+ <string>Priority Ring Tone</string>
+ <key>149</key>
+ <string>Inside Ringback</string>
+ <key>150</key>
+ <string>Priority Ringback</string>
+ <key>151</key>
+ <string>Line Busy Tone</string>
+ <key>152</key>
+ <string>Reorder Tone</string>
+ <key>153</key>
+ <string>Call Waiting Tone</string>
+ <key>154</key>
+ <string>Confirmation Tone 1</string>
+ <key>155</key>
+ <string>Confirmation Tone 2</string>
+ <key>156</key>
+ <string>Tones Off</string>
+ <key>176</key>
+ <string>Phone Key 0</string>
+ <key>177</key>
+ <string>Phone Key 1</string>
+ <key>178</key>
+ <string>Phone Key 2</string>
+ <key>179</key>
+ <string>Phone Key 3</string>
+ <key>180</key>
+ <string>Phone Key 4</string>
+ <key>181</key>
+ <string>Phone Key 5</string>
+ <key>182</key>
+ <string>Phone Key 6</string>
+ <key>183</key>
+ <string>Phone Key 7</string>
+ <key>184</key>
+ <string>Phone Key 8</string>
+ <key>185</key>
+ <string>Phone Key 9</string>
+ <key>186</key>
+ <string>Phone Key Star</string>
+ <key>187</key>
+ <string>Phone Key Pound</string>
+ <key>188</key>
+ <string>Phone Key A</string>
+ <key>189</key>
+ <string>Phone Key B</string>
+ <key>190</key>
+ <string>Phone Key C</string>
+ <key>191</key>
+ <string>Phone Key D</string>
+ </dict>
+ </dict>
+ <key>12</key>
+ <dict>
+ <key>description</key>
+ <string>Consumer</string>
+ <key>ranges</key>
+ <dict>
+ </dict>
+ <key>usages</key>
+ <dict>
+ <key>0</key>
+ <string>Unassigned</string>
+ <key>1</key>
+ <string>Consumer Control</string>
+ <key>2</key>
+ <string>Numeric Key Pad</string>
+ <key>3</key>
+ <string>Programmable Buttons</string>
+ <key>32</key>
+ <string>+10</string>
+ <key>33</key>
+ <string>+100</string>
+ <key>34</key>
+ <string>AM/PM</string>
+ <key>48</key>
+ <string>Power</string>
+ <key>49</key>
+ <string>Reset</string>
+ <key>50</key>
+ <string>Sleep</string>
+ <key>51</key>
+ <string>Sleep After</string>
+ <key>52</key>
+ <string>Sleep Mode</string>
+ <key>53</key>
+ <string>Illumination</string>
+ <key>54</key>
+ <string>Function Buttons</string>
+ <key>64</key>
+ <string>Menu</string>
+ <key>65</key>
+ <string>Menu Pick</string>
+ <key>66</key>
+ <string>Menu Up</string>
+ <key>67</key>
+ <string>Menu Down</string>
+ <key>68</key>
+ <string>Menu Left</string>
+ <key>69</key>
+ <string>Menu Right</string>
+ <key>70</key>
+ <string>Menu Escape</string>
+ <key>71</key>
+ <string>Menu Value Increase</string>
+ <key>72</key>
+ <string>Menu Value Decrease</string>
+ <key>96</key>
+ <string>Data On Screen</string>
+ <key>97</key>
+ <string>Closed Caption</string>
+ <key>98</key>
+ <string>Closed Caption Select</string>
+ <key>99</key>
+ <string>VCR/TV</string>
+ <key>100</key>
+ <string>Broadcast Mode</string>
+ <key>101</key>
+ <string>Snapshot</string>
+ <key>102</key>
+ <string>Still</string>
+ <key>128</key>
+ <string>Selection</string>
+ <key>129</key>
+ <string>Assign Selection</string>
+ <key>130</key>
+ <string>Mode Step</string>
+ <key>131</key>
+ <string>Recall Last</string>
+ <key>132</key>
+ <string>Enter Channel</string>
+ <key>133</key>
+ <string>Order Movie</string>
+ <key>134</key>
+ <string>Channel</string>
+ <key>135</key>
+ <string>Media Selection</string>
+ <key>136</key>
+ <string>Media Select Computer</string>
+ <key>137</key>
+ <string>Media Select TV</string>
+ <key>138</key>
+ <string>Media Select WWW</string>
+ <key>139</key>
+ <string>Media Select DVD</string>
+ <key>140</key>
+ <string>Media Select Telephone</string>
+ <key>141</key>
+ <string>Media Select Program Guide</string>
+ <key>142</key>
+ <string>Media Select Video Phone</string>
+ <key>143</key>
+ <string>Media Select Games</string>
+ <key>144</key>
+ <string>Media Select Messages</string>
+ <key>145</key>
+ <string>Media Select CD</string>
+ <key>146</key>
+ <string>Media Select VCR</string>
+ <key>147</key>
+ <string>Media Select Tuner</string>
+ <key>148</key>
+ <string>Quit</string>
+ <key>149</key>
+ <string>Help</string>
+ <key>150</key>
+ <string>Media Select Tape</string>
+ <key>151</key>
+ <string>Media Select Cable</string>
+ <key>152</key>
+ <string>Media Select Satellite</string>
+ <key>153</key>
+ <string>Media Select Security</string>
+ <key>154</key>
+ <string>Media Select Home</string>
+ <key>155</key>
+ <string>Media Select Call</string>
+ <key>156</key>
+ <string>Channel Increment</string>
+ <key>157</key>
+ <string>Channel Decrement</string>
+ <key>158</key>
+ <string>Media Select SAP</string>
+ <key>160</key>
+ <string>VCR Plus</string>
+ <key>161</key>
+ <string>Once</string>
+ <key>162</key>
+ <string>Daily</string>
+ <key>163</key>
+ <string>Weekly</string>
+ <key>164</key>
+ <string>Monthly</string>
+ <key>176</key>
+ <string>Play</string>
+ <key>177</key>
+ <string>Pause</string>
+ <key>178</key>
+ <string>Record</string>
+ <key>179</key>
+ <string>Fast Forward</string>
+ <key>180</key>
+ <string>Rewind</string>
+ <key>181</key>
+ <string>Scan Next Track</string>
+ <key>182</key>
+ <string>Scan Previous Track</string>
+ <key>183</key>
+ <string>Stop</string>
+ <key>184</key>
+ <string>Eject</string>
+ <key>185</key>
+ <string>Random Play</string>
+ <key>186</key>
+ <string>Select DisC</string>
+ <key>187</key>
+ <string>Enter Disc</string>
+ <key>188</key>
+ <string>Repeat</string>
+ <key>189</key>
+ <string>Tracking</string>
+ <key>190</key>
+ <string>Track Normal</string>
+ <key>191</key>
+ <string>Slow Tracking</string>
+ <key>192</key>
+ <string>Frame Forward</string>
+ <key>193</key>
+ <string>Frame Back</string>
+ <key>194</key>
+ <string>Mark</string>
+ <key>195</key>
+ <string>Clear Mark</string>
+ <key>196</key>
+ <string>Repeat From Mark</string>
+ <key>197</key>
+ <string>Return To Mark</string>
+ <key>198</key>
+ <string>Search Mark Forward</string>
+ <key>199</key>
+ <string>Search Mark Backwards</string>
+ <key>200</key>
+ <string>Counter Reset</string>
+ <key>201</key>
+ <string>Show Counter</string>
+ <key>202</key>
+ <string>Tracking Increment</string>
+ <key>203</key>
+ <string>Tracking Decrement</string>
+ <key>205</key>
+ <string>Pause/Play</string>
+ <key>224</key>
+ <string>Volume</string>
+ <key>225</key>
+ <string>Balance</string>
+ <key>226</key>
+ <string>Mute</string>
+ <key>227</key>
+ <string>Bass</string>
+ <key>228</key>
+ <string>Treble</string>
+ <key>229</key>
+ <string>Bass Boost</string>
+ <key>230</key>
+ <string>Surround Mode</string>
+ <key>231</key>
+ <string>Loudness</string>
+ <key>232</key>
+ <string>MPX</string>
+ <key>233</key>
+ <string>Volume Up</string>
+ <key>234</key>
+ <string>Volume Down</string>
+ <key>240</key>
+ <string>Speed Select</string>
+ <key>241</key>
+ <string>Playback Speed</string>
+ <key>242</key>
+ <string>Standard Play</string>
+ <key>243</key>
+ <string>Long Play</string>
+ <key>244</key>
+ <string>Extended Play</string>
+ <key>245</key>
+ <string>Slow</string>
+ <key>256</key>
+ <string>Fan Enable</string>
+ <key>257</key>
+ <string>Fan Speed</string>
+ <key>258</key>
+ <string>Light</string>
+ <key>259</key>
+ <string>Light Illumination Level</string>
+ <key>260</key>
+ <string>Climate Control Enable</string>
+ <key>261</key>
+ <string>Room Temperature</string>
+ <key>262</key>
+ <string>Security Enable</string>
+ <key>263</key>
+ <string>Fire Alarm</string>
+ <key>264</key>
+ <string>Police Alarm</string>
+ <key>336</key>
+ <string>Balance Right</string>
+ <key>337</key>
+ <string>Balance Left</string>
+ <key>338</key>
+ <string>Bass Increment</string>
+ <key>339</key>
+ <string>Bass Decrement</string>
+ <key>340</key>
+ <string>Treble Increment</string>
+ <key>341</key>
+ <string>Treble Decrement</string>
+ <key>352</key>
+ <string>Speaker System</string>
+ <key>353</key>
+ <string>Channel Left</string>
+ <key>354</key>
+ <string>Channel Right</string>
+ <key>355</key>
+ <string>Channel Center</string>
+ <key>356</key>
+ <string>Channel Front</string>
+ <key>357</key>
+ <string>Channel Center Front</string>
+ <key>358</key>
+ <string>Channel Side</string>
+ <key>359</key>
+ <string>Channel Surround</string>
+ <key>360</key>
+ <string>Channel Low Frequency Enhancement</string>
+ <key>361</key>
+ <string>Channel Top</string>
+ <key>362</key>
+ <string>Channel Unknown</string>
+ <key>368</key>
+ <string>Sub-channel</string>
+ <key>369</key>
+ <string>Sub-channel Increment</string>
+ <key>370</key>
+ <string>Sub-channel Decrement</string>
+ <key>371</key>
+ <string>Alternate Audio Increment</string>
+ <key>372</key>
+ <string>Alternate Audio Decrement</string>
+ <key>384</key>
+ <string>Application Launch Buttons</string>
+ <key>385</key>
+ <string>AL Launch Button Configuration Tool</string>
+ <key>386</key>
+ <string>AL Programmable Button Configuration</string>
+ <key>387</key>
+ <string>AL Consumer Control Configuration</string>
+ <key>388</key>
+ <string>AL Word Processor</string>
+ <key>389</key>
+ <string>AL Text Editor</string>
+ <key>390</key>
+ <string>AL Spreadsheet</string>
+ <key>391</key>
+ <string>AL Graphics Editor</string>
+ <key>392</key>
+ <string>AL Presentation App</string>
+ <key>393</key>
+ <string>AL Database App</string>
+ <key>394</key>
+ <string>AL Email Reader</string>
+ <key>395</key>
+ <string>AL Newsreader</string>
+ <key>396</key>
+ <string>AL Voicemail</string>
+ <key>397</key>
+ <string>AL Contacts/Address Book</string>
+ <key>398</key>
+ <string>AL Calendar/Schedule</string>
+ <key>399</key>
+ <string>AL Task/Project Manager</string>
+ <key>400</key>
+ <string>AL Log/Journal/Timecard</string>
+ <key>401</key>
+ <string>AL Checkbook/Finance</string>
+ <key>402</key>
+ <string>AL Calculator</string>
+ <key>403</key>
+ <string>AL A/V Capture/Playback</string>
+ <key>404</key>
+ <string>AL Local Machine Browser</string>
+ <key>405</key>
+ <string>AL LAN/WAN Browser</string>
+ <key>406</key>
+ <string>AL Internet Browser</string>
+ <key>407</key>
+ <string>AL Remote Networking/ISP Connect</string>
+ <key>408</key>
+ <string>AL Network Conference</string>
+ <key>409</key>
+ <string>AL Network Chat</string>
+ <key>410</key>
+ <string>AL Telephony/Dialer</string>
+ <key>411</key>
+ <string>AL Logon</string>
+ <key>412</key>
+ <string>AL Logoff</string>
+ <key>413</key>
+ <string>AL Logon/Logoff</string>
+ <key>414</key>
+ <string>AL Terminal Lock/Screensaver</string>
+ <key>415</key>
+ <string>AL Control Panel</string>
+ <key>416</key>
+ <string>AL Command Line Processor/Run</string>
+ <key>417</key>
+ <string>AL Process/Task Manager</string>
+ <key>418</key>
+ <string>AL Select Tast/Application</string>
+ <key>419</key>
+ <string>AL Next Task/Application</string>
+ <key>420</key>
+ <string>AL Previous Task/Application</string>
+ <key>421</key>
+ <string>AL Preemptive Halt Task/Application</string>
+ <key>423</key>
+ <string>AL My Documents</string>
+ <key>427</key>
+ <string>AC Spell</string>
+ <key>438</key>
+ <string>AL My Pictures</string>
+ <key>439</key>
+ <string>AL My Music</string>
+ <key>512</key>
+ <string>Generic GUI Application Controls</string>
+ <key>513</key>
+ <string>AC New</string>
+ <key>514</key>
+ <string>AC Open</string>
+ <key>515</key>
+ <string>AC Close</string>
+ <key>516</key>
+ <string>AC Exit</string>
+ <key>517</key>
+ <string>AC Maximize</string>
+ <key>518</key>
+ <string>AC Minimize</string>
+ <key>519</key>
+ <string>AC Save</string>
+ <key>520</key>
+ <string>AC Print</string>
+ <key>521</key>
+ <string>AC Properties</string>
+ <key>538</key>
+ <string>AC Undo</string>
+ <key>539</key>
+ <string>AC Copy</string>
+ <key>540</key>
+ <string>AC Cut</string>
+ <key>541</key>
+ <string>AC Paste</string>
+ <key>542</key>
+ <string>AC Select All</string>
+ <key>543</key>
+ <string>AC Find</string>
+ <key>544</key>
+ <string>AC Find and Replace</string>
+ <key>545</key>
+ <string>AC Search</string>
+ <key>546</key>
+ <string>AC Go To</string>
+ <key>547</key>
+ <string>AC Home</string>
+ <key>548</key>
+ <string>AC Back</string>
+ <key>549</key>
+ <string>AC Forward</string>
+ <key>550</key>
+ <string>AC Stop</string>
+ <key>551</key>
+ <string>AC Refresh</string>
+ <key>552</key>
+ <string>AC Previous Link</string>
+ <key>553</key>
+ <string>AC Next Link</string>
+ <key>554</key>
+ <string>AC Bookmarks</string>
+ <key>555</key>
+ <string>AC History</string>
+ <key>556</key>
+ <string>AC Subscriptions</string>
+ <key>557</key>
+ <string>AC Zoom In</string>
+ <key>558</key>
+ <string>AC Zoom Out</string>
+ <key>559</key>
+ <string>AC Zoom</string>
+ <key>560</key>
+ <string>AC Full Screen View</string>
+ <key>561</key>
+ <string>AC Normal View</string>
+ <key>562</key>
+ <string>AC View Toggle</string>
+ <key>563</key>
+ <string>AC Scroll Up</string>
+ <key>564</key>
+ <string>AC Scroll Down</string>
+ <key>565</key>
+ <string>AC Scroll</string>
+ <key>566</key>
+ <string>AC Pan Left</string>
+ <key>567</key>
+ <string>AC Pan Right</string>
+ <key>568</key>
+ <string>AC Pan</string>
+ <key>569</key>
+ <string>AC New Window</string>
+ <key>570</key>
+ <string>AC Tile Horizontally</string>
+ <key>571</key>
+ <string>AC Tile Vertically</string>
+ <key>572</key>
+ <string>AC Format</string>
+ <key>633</key>
+ <string>AC Redo</string>
+ <key>649</key>
+ <string>AC Reply</string>
+ <key>651</key>
+ <string>AC Fwd</string>
+ <key>652</key>
+ <string>AC Send</string>
+ </dict>
+ </dict>
+ <key>13</key>
+ <dict>
+ <key>description</key>
+ <string>Digitizer</string>
+ <key>ranges</key>
+ <dict>
+ </dict>
+ <key>usages</key>
+ <dict>
+ <key>0</key>
+ <string>Undefined</string>
+ <key>1</key>
+ <string>Digitizer</string>
+ <key>2</key>
+ <string>Pen</string>
+ <key>3</key>
+ <string>Light Pen</string>
+ <key>4</key>
+ <string>Touch Screen</string>
+ <key>5</key>
+ <string>Touch Pad</string>
+ <key>6</key>
+ <string>White Board</string>
+ <key>7</key>
+ <string>Coordinate Measuring Machine</string>
+ <key>8</key>
+ <string>3-D Digitizer</string>
+ <key>9</key>
+ <string>Stereo Plotter</string>
+ <key>10</key>
+ <string>Articulated Arm</string>
+ <key>11</key>
+ <string>Armature</string>
+ <key>12</key>
+ <string>Multiple Point Digitizer</string>
+ <key>13</key>
+ <string>Free Space Wand</string>
+ <key>32</key>
+ <string>Stylus</string>
+ <key>33</key>
+ <string>Puck</string>
+ <key>34</key>
+ <string>Finger</string>
+ <key>48</key>
+ <string>Tip Pressure</string>
+ <key>49</key>
+ <string>Barrel Pressure</string>
+ <key>50</key>
+ <string>In Range</string>
+ <key>51</key>
+ <string>Touch</string>
+ <key>52</key>
+ <string>Untouch</string>
+ <key>53</key>
+ <string>Tap</string>
+ <key>54</key>
+ <string>Quality</string>
+ <key>55</key>
+ <string>Data Valid</string>
+ <key>56</key>
+ <string>Transducer Index</string>
+ <key>57</key>
+ <string>Tablet Function Keys</string>
+ <key>58</key>
+ <string>Program Change Keys</string>
+ <key>59</key>
+ <string>Battery Strength</string>
+ <key>60</key>
+ <string>Invert</string>
+ <key>61</key>
+ <string>X Tilt</string>
+ <key>62</key>
+ <string>Y Tilt</string>
+ <key>63</key>
+ <string>Azimuth</string>
+ <key>64</key>
+ <string>Altitude</string>
+ <key>65</key>
+ <string>Twist</string>
+ <key>66</key>
+ <string>Tip Switch</string>
+ <key>67</key>
+ <string>Secondary Tip Switch</string>
+ <key>68</key>
+ <string>Barrel Switch</string>
+ <key>69</key>
+ <string>Eraser</string>
+ <key>70</key>
+ <string>Tablet Pick</string>
+ </dict>
+ </dict>
+ <key>15</key>
+ <dict>
+ <key>description</key>
+ <string>Physical Interface Device</string>
+ <key>ranges</key>
+ <dict>
+ </dict>
+ <key>usages</key>
+ <dict>
+ </dict>
+ </dict>
+ <key>16</key>
+ <dict>
+ <key>default</key>
+ <string>Unicode Char u%04x</string>
+ <key>description</key>
+ <string>Unicode</string>
+ <key>ranges</key>
+ <dict>
+ </dict>
+ <key>usages</key>
+ <dict>
+ </dict>
+ </dict>
+ <key>20</key>
+ <dict>
+ <key>description</key>
+ <string>Alphnumeric Display</string>
+ <key>ranges</key>
+ <dict>
+ </dict>
+ <key>usages</key>
+ <dict>
+ <key>0</key>
+ <string>Undefined</string>
+ <key>1</key>
+ <string>Alphanumeric Display</string>
+ <key>32</key>
+ <string>Display Attributes Report</string>
+ <key>33</key>
+ <string>ASCII Character Set</string>
+ <key>34</key>
+ <string>Data Read Back</string>
+ <key>35</key>
+ <string>Font Read Back</string>
+ <key>36</key>
+ <string>Display Control Report</string>
+ <key>37</key>
+ <string>Clear Display</string>
+ <key>38</key>
+ <string>Display Enable</string>
+ <key>39</key>
+ <string>Screen Saver Delay</string>
+ <key>40</key>
+ <string>Screen Saver Enable</string>
+ <key>41</key>
+ <string>Vertical Scroll</string>
+ <key>42</key>
+ <string>Horizontal Scroll</string>
+ <key>43</key>
+ <string>Character Report</string>
+ <key>44</key>
+ <string>Display Data</string>
+ <key>45</key>
+ <string>Display Status</string>
+ <key>46</key>
+ <string>Stat Not Ready</string>
+ <key>47</key>
+ <string>Stat Ready</string>
+ <key>48</key>
+ <string>Err Not a loadable character</string>
+ <key>49</key>
+ <string>Err Font data cannot be read</string>
+ <key>50</key>
+ <string>Cursor Position Report</string>
+ <key>51</key>
+ <string>Row</string>
+ <key>52</key>
+ <string>Column</string>
+ <key>53</key>
+ <string>Rows</string>
+ <key>54</key>
+ <string>Columns</string>
+ <key>55</key>
+ <string>Cursor Pixel Positioning</string>
+ <key>56</key>
+ <string>Cursor Mode</string>
+ <key>57</key>
+ <string>Cursor Enable</string>
+ <key>58</key>
+ <string>Cursor Blink</string>
+ <key>59</key>
+ <string>Font Report</string>
+ <key>60</key>
+ <string>Font Data</string>
+ <key>61</key>
+ <string>Character Width</string>
+ <key>62</key>
+ <string>Character Height</string>
+ <key>63</key>
+ <string>Character Spacing Horizontal</string>
+ <key>64</key>
+ <string>Character Spacing Vertical</string>
+ <key>65</key>
+ <string>Unicode Character Set</string>
+ </dict>
+ </dict>
+ <key>128</key>
+ <dict>
+ <key>description</key>
+ <string>Monitor</string>
+ <key>ranges</key>
+ <dict>
+ </dict>
+ <key>usages</key>
+ <dict>
+ <key>0</key>
+ <string>Undefined</string>
+ <key>1</key>
+ <string>Monitor Control</string>
+ <key>2</key>
+ <string>EDID Information</string>
+ <key>3</key>
+ <string>VDIF Information</string>
+ <key>4</key>
+ <string>VESA Version</string>
+ <key>5</key>
+ <string>On Screen Display</string>
+ <key>6</key>
+ <string>Auto Size Center</string>
+ <key>7</key>
+ <string>Polarity Horz Synch</string>
+ <key>8</key>
+ <string>Polarity Vert Synch</string>
+ <key>9</key>
+ <string>Sync Type</string>
+ <key>10</key>
+ <string>Screen Position</string>
+ <key>11</key>
+ <string>Horizontal Frequency</string>
+ <key>12</key>
+ <string>Vertical Frequency</string>
+ </dict>
+ </dict>
+ <key>129</key>
+ <dict>
+ <key>default</key>
+ <string>ENUM %d</string>
+ <key>description</key>
+ <string>Monitor Enumerated Values</string>
+ <key>ranges</key>
+ <dict>
+ </dict>
+ <key>usages</key>
+ <dict>
+ <key>0</key>
+ <string>unassigned</string>
+ </dict>
+ </dict>
+ <key>130</key>
+ <dict>
+ <key>description</key>
+ <string>VESA Virtual Controls</string>
+ <key>ranges</key>
+ <dict>
+ </dict>
+ <key>usages</key>
+ <dict>
+ <key>16</key>
+ <string>Brightness</string>
+ <key>18</key>
+ <string>Contrast</string>
+ <key>22</key>
+ <string>Video Gain Red</string>
+ <key>24</key>
+ <string>Video Gain Green</string>
+ <key>26</key>
+ <string>Video Gain Blue</string>
+ <key>28</key>
+ <string>Focus</string>
+ <key>32</key>
+ <string>Horizontal Position</string>
+ <key>34</key>
+ <string>Horizontal Size</string>
+ <key>36</key>
+ <string>Horizontal Pincushion</string>
+ <key>38</key>
+ <string>Horizontal Pincushion Balance</string>
+ <key>40</key>
+ <string>Horizontal Misconvergence</string>
+ <key>42</key>
+ <string>Horizontal Linearity</string>
+ <key>44</key>
+ <string>Horizontal Linearity Balance</string>
+ <key>48</key>
+ <string>Vertical Position</string>
+ <key>50</key>
+ <string>Vertical Size</string>
+ <key>52</key>
+ <string>Vertical Pincushion</string>
+ <key>54</key>
+ <string>Vertical Pincushion Balance</string>
+ <key>56</key>
+ <string>Vertical Misconvergence</string>
+ <key>58</key>
+ <string>Vertical Linearity</string>
+ <key>60</key>
+ <string>Vertical Linearity Balance</string>
+ <key>64</key>
+ <string>Parallelogram Distortion</string>
+ <key>66</key>
+ <string>Trapezoidal Distortion</string>
+ <key>68</key>
+ <string>Tilt</string>
+ <key>70</key>
+ <string>Top Corner Distortion Control</string>
+ <key>72</key>
+ <string>Top Corner Distortion Balance</string>
+ <key>74</key>
+ <string>Bottom Corner Distortion Control</string>
+ <key>76</key>
+ <string>Bottom Corner Distortion Balance</string>
+ <key>86</key>
+ <string>MoirHorizontal</string>
+ <key>88</key>
+ <string>MoirVertical</string>
+ <key>94</key>
+ <string>Input Level Select</string>
+ <key>96</key>
+ <string>Input Source Select</string>
+ <key>98</key>
+ <string>Stereo Mode</string>
+ <key>108</key>
+ <string>Video Black Level Red</string>
+ <key>110</key>
+ <string>Video Black Level Green</string>
+ <key>112</key>
+ <string>Video Black Level Blue</string>
+ </dict>
+ </dict>
+ <key>131</key>
+ <dict>
+ <key>description</key>
+ <string>VESA Command</string>
+ <key>ranges</key>
+ <dict>
+ </dict>
+ <key>usages</key>
+ <dict>
+ <key>0</key>
+ <string>Undefined</string>
+ <key>1</key>
+ <string>Settings</string>
+ <key>2</key>
+ <string>Degauss</string>
+ </dict>
+ </dict>
+ <key>132</key>
+ <dict>
+ <key>description</key>
+ <string>Power Device</string>
+ <key>ranges</key>
+ <dict>
+ </dict>
+ <key>usages</key>
+ <dict>
+ <key>0</key>
+ <string>Undefined</string>
+ <key>1</key>
+ <string>iName</string>
+ <key>2</key>
+ <string>PresentStatus</string>
+ <key>3</key>
+ <string>ChangedStatus</string>
+ <key>4</key>
+ <string>UPS</string>
+ <key>5</key>
+ <string>PowerSupply</string>
+ <key>16</key>
+ <string>BatterySystem</string>
+ <key>17</key>
+ <string>BatterySystemID</string>
+ <key>18</key>
+ <string>Battery</string>
+ <key>19</key>
+ <string>BatteryID</string>
+ <key>20</key>
+ <string>Charger</string>
+ <key>21</key>
+ <string>ChargerID</string>
+ <key>22</key>
+ <string>PowerConverter</string>
+ <key>23</key>
+ <string>PowerConverterID</string>
+ <key>24</key>
+ <string>OutletSystem</string>
+ <key>25</key>
+ <string>OutletSystemID</string>
+ <key>26</key>
+ <string>Input</string>
+ <key>27</key>
+ <string>InputID</string>
+ <key>28</key>
+ <string>Output</string>
+ <key>29</key>
+ <string>OutputID</string>
+ <key>30</key>
+ <string>Flow</string>
+ <key>31</key>
+ <string>FlowID</string>
+ <key>32</key>
+ <string>Outlet</string>
+ <key>33</key>
+ <string>OutletID</string>
+ <key>34</key>
+ <string>Gang</string>
+ <key>35</key>
+ <string>GangID</string>
+ <key>36</key>
+ <string>Sink</string>
+ <key>37</key>
+ <string>SinkID</string>
+ <key>48</key>
+ <string>Voltage</string>
+ <key>49</key>
+ <string>Current</string>
+ <key>50</key>
+ <string>Frequency</string>
+ <key>51</key>
+ <string>ApparentPower</string>
+ <key>52</key>
+ <string>ActivePower</string>
+ <key>53</key>
+ <string>PercentLoad</string>
+ <key>54</key>
+ <string>Temperature</string>
+ <key>55</key>
+ <string>Humidity</string>
+ <key>64</key>
+ <string>ConfigVoltage</string>
+ <key>65</key>
+ <string>ConfigCurrent</string>
+ <key>66</key>
+ <string>ConfigFrequency</string>
+ <key>67</key>
+ <string>ConfigApparentPower</string>
+ <key>68</key>
+ <string>ConfigActivePower</string>
+ <key>69</key>
+ <string>ConfigPercentLoad</string>
+ <key>70</key>
+ <string>ConfigTemperature</string>
+ <key>71</key>
+ <string>ConfigHumidity</string>
+ <key>80</key>
+ <string>SwitchOnControl</string>
+ <key>81</key>
+ <string>SwitchOffControl</string>
+ <key>82</key>
+ <string>ToggleControl</string>
+ <key>83</key>
+ <string>LowVoltageTransfer</string>
+ <key>84</key>
+ <string>HighVoltageTransfer</string>
+ <key>85</key>
+ <string>DelayBeforeReboot</string>
+ <key>86</key>
+ <string>DelayBeforeStartup</string>
+ <key>87</key>
+ <string>DelayBeforeShutdown</string>
+ <key>88</key>
+ <string>Test</string>
+ <key>89</key>
+ <string>Vendorspecificcommand</string>
+ <key>96</key>
+ <string>Present</string>
+ <key>97</key>
+ <string>Good</string>
+ <key>98</key>
+ <string>InternalFailure</string>
+ <key>99</key>
+ <string>VoltageOutOfRange</string>
+ <key>100</key>
+ <string>FrequencyOutOfRange</string>
+ <key>101</key>
+ <string>Overload</string>
+ <key>102</key>
+ <string>OverCharged</string>
+ <key>103</key>
+ <string>OverTemperature</string>
+ <key>104</key>
+ <string>ShutdownRequested</string>
+ <key>105</key>
+ <string>ShutdownImminent</string>
+ <key>106</key>
+ <string>VendorSpecificAnswerValid</string>
+ <key>107</key>
+ <string>SwitchOn/Off</string>
+ <key>108</key>
+ <string>Switcheble</string>
+ <key>109</key>
+ <string>Used</string>
+ <key>110</key>
+ <string>Boost</string>
+ <key>111</key>
+ <string>Buck</string>
+ <key>112</key>
+ <string>Initialized</string>
+ <key>113</key>
+ <string>Tested</string>
+ </dict>
+ </dict>
+ <key>133</key>
+ <dict>
+ <key>description</key>
+ <string>Battery System</string>
+ <key>ranges</key>
+ <dict>
+ </dict>
+ <key>usages</key>
+ <dict>
+ <key>0</key>
+ <string>Undefined</string>
+ <key>1</key>
+ <string>SMBBatteryMode</string>
+ <key>2</key>
+ <string>SMBBatteryStatus</string>
+ <key>3</key>
+ <string>SMBAlarmWarning</string>
+ <key>4</key>
+ <string>SMBChargerMode</string>
+ <key>5</key>
+ <string>SMBChargerStatus</string>
+ <key>6</key>
+ <string>SMBChargerSpecInfo</string>
+ <key>7</key>
+ <string>SMBSelectorState</string>
+ <key>8</key>
+ <string>SMBSelectorPreset</string>
+ <key>9</key>
+ <string>SMBSelectorInfo</string>
+ <key>16</key>
+ <string>OptionalMfgFunction1</string>
+ <key>17</key>
+ <string>OptionalMfgFunction2</string>
+ <key>18</key>
+ <string>OptionalMfgFunction3</string>
+ <key>19</key>
+ <string>OptionalMfgFunction4</string>
+ <key>20</key>
+ <string>OptionalMfgFunction5</string>
+ <key>21</key>
+ <string>ConnectionToSMBus</string>
+ <key>22</key>
+ <string>OutputConnection</string>
+ <key>23</key>
+ <string>ChargerConnection</string>
+ <key>24</key>
+ <string>BatteryInsertion</string>
+ <key>25</key>
+ <string>Usenext</string>
+ <key>26</key>
+ <string>OKToUse</string>
+ <key>40</key>
+ <string>ManufacturerAccess</string>
+ <key>41</key>
+ <string>RemainingCapacityLimit</string>
+ <key>42</key>
+ <string>RemainingTimeLimit</string>
+ <key>43</key>
+ <string>AtRate</string>
+ <key>44</key>
+ <string>CapacityMode</string>
+ <key>45</key>
+ <string>BroadcastToCharger</string>
+ <key>46</key>
+ <string>PrimaryBattery</string>
+ <key>47</key>
+ <string>ChargeController</string>
+ <key>64</key>
+ <string>TerminateCharge</string>
+ <key>65</key>
+ <string>TermminateDischarge</string>
+ <key>66</key>
+ <string>BelowRemainingCapacityLimit</string>
+ <key>67</key>
+ <string>RemainingTimeLimitExpired</string>
+ <key>68</key>
+ <string>Charging</string>
+ <key>69</key>
+ <string>Discharging</string>
+ <key>70</key>
+ <string>FullyCharged</string>
+ <key>71</key>
+ <string>FullyDischarged</string>
+ <key>72</key>
+ <string>ConditionningFlag</string>
+ <key>73</key>
+ <string>AtRateOK</string>
+ <key>74</key>
+ <string>SMBErrorCode</string>
+ <key>75</key>
+ <string>NeedReplacement</string>
+ <key>96</key>
+ <string>AtRateTimeToFull</string>
+ <key>97</key>
+ <string>AtRateTimeToEmpty</string>
+ <key>98</key>
+ <string>AverageCurrent</string>
+ <key>99</key>
+ <string>Maxerror</string>
+ <key>100</key>
+ <string>RelativeStateOfCharge</string>
+ <key>101</key>
+ <string>AbsoluteStateOfCharge</string>
+ <key>102</key>
+ <string>RemainingCapacity</string>
+ <key>103</key>
+ <string>FullChargeCapacity</string>
+ <key>104</key>
+ <string>RunTimeToEmpty</string>
+ <key>105</key>
+ <string>AverageTimeToEmpty</string>
+ <key>106</key>
+ <string>AverageTimeToFull</string>
+ <key>107</key>
+ <string>CycleCount</string>
+ <key>128</key>
+ <string>BattPackModelLevel</string>
+ <key>129</key>
+ <string>InternalChargeController</string>
+ <key>130</key>
+ <string>PrimaryBatterySupport</string>
+ <key>131</key>
+ <string>DesignCapacity</string>
+ <key>132</key>
+ <string>SpecificationInfo</string>
+ <key>133</key>
+ <string>ManufacturerDate</string>
+ <key>134</key>
+ <string>SerialNumber</string>
+ <key>135</key>
+ <string>iManufacturerName</string>
+ <key>136</key>
+ <string>iDevicename</string>
+ <key>137</key>
+ <string>iDeviceChemistery</string>
+ <key>138</key>
+ <string>iManufacturerData</string>
+ <key>139</key>
+ <string>Rechargeable</string>
+ <key>140</key>
+ <string>WarningCapacityLimit</string>
+ <key>141</key>
+ <string>CapacityGranularity1</string>
+ <key>142</key>
+ <string>CapacityGranularity2</string>
+ <key>192</key>
+ <string>InhibitCharge</string>
+ <key>193</key>
+ <string>EnablePolling</string>
+ <key>194</key>
+ <string>ResetToZero</string>
+ <key>208</key>
+ <string>ACPresent</string>
+ <key>209</key>
+ <string>BatteryPresent</string>
+ <key>210</key>
+ <string>PowerFail</string>
+ <key>211</key>
+ <string>AlarmInhibited</string>
+ <key>212</key>
+ <string>ThermistorUnderRange</string>
+ <key>213</key>
+ <string>ThermistorHot</string>
+ <key>214</key>
+ <string>ThermistorCold</string>
+ <key>215</key>
+ <string>ThermistorOverRange</string>
+ <key>216</key>
+ <string>VoltageOutOfRange</string>
+ <key>217</key>
+ <string>CurrentOutOfRange</string>
+ <key>218</key>
+ <string>CurrentNotRegulated</string>
+ <key>219</key>
+ <string>VoltageNotRegulated</string>
+ <key>220</key>
+ <string>MasterMode</string>
+ <key>221</key>
+ <string>ChargerBattery/HostControlled</string>
+ <key>240</key>
+ <string>ChargerSpecInfo</string>
+ <key>241</key>
+ <string>ChargerSpecRef</string>
+ <key>242</key>
+ <string>Level2</string>
+ <key>243</key>
+ <string>Level3</string>
+ </dict>
+ </dict>
+ <key>140</key>
+ <dict>
+ <key>description</key>
+ <string>Bar Code Scanner</string>
+ <key>ranges</key>
+ <dict>
+ </dict>
+ <key>usages</key>
+ <dict>
+ </dict>
+ </dict>
+ <key>141</key>
+ <dict>
+ <key>description</key>
+ <string>Scale Device</string>
+ <key>ranges</key>
+ <dict>
+ </dict>
+ <key>usages</key>
+ <dict>
+ </dict>
+ </dict>
+ <key>144</key>
+ <dict>
+ <key>description</key>
+ <string>Camera Control</string>
+ <key>ranges</key>
+ <dict>
+ </dict>
+ <key>usages</key>
+ <dict>
+ </dict>
+ </dict>
+ <key>145</key>
+ <dict>
+ <key>description</key>
+ <string>Arcade Device</string>
+ <key>ranges</key>
+ <dict>
+ </dict>
+ <key>usages</key>
+ <dict>
+ <key>233</key>
+ <string>Base Up</string>
+ <key>234</key>
+ <string>Base Down</string>
+ </dict>
+ </dict>
+ </dict>
+</plist>
diff --git a/lib/DDHidUsage.h b/lib/DDHidUsage.h
new file mode 100644
index 0000000..8dc5654
--- /dev/null
+++ b/lib/DDHidUsage.h
@@ -0,0 +1,52 @@
+/*
+ * Copyright (c) 2007 Dave Dribin
+ *
+ * Permission is hereby granted, free of charge, to any person
+ * obtaining a copy of this software and associated documentation
+ * files (the "Software"), to deal in the Software without
+ * restriction, including without limitation the rights to use, copy,
+ * modify, merge, publish, distribute, sublicense, and/or sell copies
+ * of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be
+ * included in all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+ * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
+ * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
+ * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
+ * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+ * SOFTWARE.
+ */
+
+#import <Cocoa/Cocoa.h>
+
+
+@interface DDHidUsage : NSObject
+{
+ unsigned mUsagePage;
+ unsigned mUsageId;
+}
+
++ (DDHidUsage *) usageWithUsagePage: (unsigned) usagePage
+ usageId: (unsigned) usageId;
+
+- (id) initWithUsagePage: (unsigned) usagePage
+ usageId: (unsigned) usageId;
+
+- (unsigned) usagePage;
+
+- (unsigned) usageId;
+
+- (NSString *) usageName;
+
+- (NSString *) usageNameWithIds;
+
+- (NSString *) description;
+
+- (BOOL) isEqualToUsagePage: (unsigned) usagePage usageId: (unsigned) usageId;
+
+@end
diff --git a/lib/DDHidUsage.m b/lib/DDHidUsage.m
new file mode 100644
index 0000000..8fe2c11
--- /dev/null
+++ b/lib/DDHidUsage.m
@@ -0,0 +1,84 @@
+/*
+ * Copyright (c) 2007 Dave Dribin
+ *
+ * Permission is hereby granted, free of charge, to any person
+ * obtaining a copy of this software and associated documentation
+ * files (the "Software"), to deal in the Software without
+ * restriction, including without limitation the rights to use, copy,
+ * modify, merge, publish, distribute, sublicense, and/or sell copies
+ * of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be
+ * included in all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+ * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
+ * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
+ * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
+ * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+ * SOFTWARE.
+ */
+
+#import "DDHidUsage.h"
+#import "DDHidUsageTables.h"
+
+@implementation DDHidUsage
+
++ (DDHidUsage *) usageWithUsagePage: (unsigned) usagePage
+ usageId: (unsigned) usageId;
+{
+ return [[[self alloc] initWithUsagePage: usagePage usageId: usageId]
+ autorelease];
+}
+
+- (id) initWithUsagePage: (unsigned) usagePage
+ usageId: (unsigned) usageId;
+{
+ self = [super init];
+ if (self == nil)
+ return nil;
+
+ mUsagePage = usagePage;
+ mUsageId = usageId;
+
+ return self;
+}
+
+- (unsigned) usagePage;
+{
+ return mUsagePage;
+}
+
+- (unsigned) usageId;
+{
+ return mUsageId;
+}
+
+- (NSString *) usageName;
+{
+ DDHidUsageTables * usageTables = [DDHidUsageTables standardUsageTables];
+ return
+ [usageTables descriptionForUsagePage: mUsagePage
+ usage: mUsageId];
+}
+
+- (NSString *) usageNameWithIds;
+{
+ return [NSString stringWithFormat: @"%@ (0x%04x : 0x%04x)",
+ [self usageName], mUsagePage, mUsageId];
+}
+
+- (NSString *) description;
+{
+ return [NSString stringWithFormat: @"HID Usage: %@", [self usageName]];
+}
+
+- (BOOL) isEqualToUsagePage: (unsigned) usagePage usageId: (unsigned) usageId;
+{
+ return ((mUsagePage == usagePage) && (mUsageId == usageId));
+}
+
+@end
diff --git a/lib/DDHidUsageTables.h b/lib/DDHidUsageTables.h
new file mode 100644
index 0000000..be8d46e
--- /dev/null
+++ b/lib/DDHidUsageTables.h
@@ -0,0 +1,40 @@
+/*
+ * Copyright (c) 2007 Dave Dribin
+ *
+ * Permission is hereby granted, free of charge, to any person
+ * obtaining a copy of this software and associated documentation
+ * files (the "Software"), to deal in the Software without
+ * restriction, including without limitation the rights to use, copy,
+ * modify, merge, publish, distribute, sublicense, and/or sell copies
+ * of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be
+ * included in all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+ * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
+ * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
+ * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
+ * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+ * SOFTWARE.
+ */
+
+#import <Cocoa/Cocoa.h>
+
+
+@interface DDHidUsageTables : NSObject
+{
+ NSDictionary * mLookupTables;
+}
+
++ (DDHidUsageTables *) standardUsageTables;
+
+- (id) initWithLookupTables: (NSDictionary *) lookupTables;
+
+- (NSString *) descriptionForUsagePage: (unsigned) usagePage
+ usage: (unsigned) usage;
+
+@end
diff --git a/lib/DDHidUsageTables.m b/lib/DDHidUsageTables.m
new file mode 100644
index 0000000..9e7703a
--- /dev/null
+++ b/lib/DDHidUsageTables.m
@@ -0,0 +1,85 @@
+/*
+ * Copyright (c) 2007 Dave Dribin
+ *
+ * Permission is hereby granted, free of charge, to any person
+ * obtaining a copy of this software and associated documentation
+ * files (the "Software"), to deal in the Software without
+ * restriction, including without limitation the rights to use, copy,
+ * modify, merge, publish, distribute, sublicense, and/or sell copies
+ * of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be
+ * included in all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+ * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
+ * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
+ * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
+ * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+ * SOFTWARE.
+ */
+
+#import "DDHidUsageTables.h"
+
+@implementation DDHidUsageTables
+
+static DDHidUsageTables * sStandardUsageTables = nil;
+
++ (DDHidUsageTables *) standardUsageTables;
+{
+ if (sStandardUsageTables == nil)
+ {
+ NSBundle * myBundle = [NSBundle bundleForClass: self];
+ NSString * usageTablesPath =
+ [myBundle pathForResource: @"DDHidStandardUsages" ofType: @"plist"];
+ NSDictionary * lookupTables =
+ [NSDictionary dictionaryWithContentsOfFile: usageTablesPath];
+ sStandardUsageTables =
+ [[DDHidUsageTables alloc] initWithLookupTables: lookupTables];
+ [sStandardUsageTables retain];
+ }
+
+ return sStandardUsageTables;
+}
+
+- (id) initWithLookupTables: (NSDictionary *) lookupTables;
+{
+ self = [super init];
+ if (self == nil)
+ return nil;
+
+ mLookupTables = [lookupTables retain];
+
+ return self;
+}
+
+- (NSString *) descriptionForUsagePage: (unsigned) usagePage
+ usage: (unsigned) usage
+{
+ NSString * usagePageString = [NSString stringWithFormat: @"%u", usagePage];
+ NSString * usageString = [NSString stringWithFormat: @"%u", usage];
+ // NSNumber * usagePageNumber = [NSNumber numberWithUnsignedInt: usagePage];
+
+ NSDictionary * usagePageLookup = [mLookupTables objectForKey: usagePageString];
+ if (usagePageLookup == nil)
+ return @"Unknown usage page";
+
+ NSDictionary * usageLookup = [usagePageLookup objectForKey: @"usages"];
+ NSString * description = [usageLookup objectForKey: usageString];
+ if (description != nil)
+ return description;
+
+ NSString * defaultUsage = [usagePageLookup objectForKey: @"default"];
+ if (defaultUsage != nil)
+ {
+ description = [NSString stringWithFormat: defaultUsage, usage];
+ return description;
+ }
+
+ return @"Unknown usage";
+}
+
+@end
diff --git a/lib/NSDictionary+DDHidExtras.h b/lib/NSDictionary+DDHidExtras.h
new file mode 100644
index 0000000..750d0d4
--- /dev/null
+++ b/lib/NSDictionary+DDHidExtras.h
@@ -0,0 +1,46 @@
+/*
+ * Copyright (c) 2007 Dave Dribin
+ *
+ * Permission is hereby granted, free of charge, to any person
+ * obtaining a copy of this software and associated documentation
+ * files (the "Software"), to deal in the Software without
+ * restriction, including without limitation the rights to use, copy,
+ * modify, merge, publish, distribute, sublicense, and/or sell copies
+ * of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be
+ * included in all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+ * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
+ * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
+ * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
+ * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+ * SOFTWARE.
+ */
+
+#import <Cocoa/Cocoa.h>
+
+
+@interface NSDictionary (DDHidExtras)
+
+- (unsigned) ddhid_unsignedForKey: (NSString *) key;
+
+- (id) ddhid_objectForString: (const char *) key;
+
+- (NSString *) ddhid_stringForString: (const char *) key;
+- (long) ddhid_longForString: (const char *) key;
+- (unsigned int) ddhid_unsignedIntForString: (const char *) key;
+- (BOOL) ddhid_boolForString: (const char *) key;
+
+@end
+
+@interface NSMutableDictionary (DDHidExtras)
+
+- (void) ddhid_setObject: (id) object forString: (const char *) key;
+- (void) ddhid_setInt: (int) i forKey: (id) key;
+
+@end
diff --git a/lib/NSDictionary+DDHidExtras.m b/lib/NSDictionary+DDHidExtras.m
new file mode 100644
index 0000000..4864a48
--- /dev/null
+++ b/lib/NSDictionary+DDHidExtras.m
@@ -0,0 +1,81 @@
+/*
+ * Copyright (c) 2007 Dave Dribin
+ *
+ * Permission is hereby granted, free of charge, to any person
+ * obtaining a copy of this software and associated documentation
+ * files (the "Software"), to deal in the Software without
+ * restriction, including without limitation the rights to use, copy,
+ * modify, merge, publish, distribute, sublicense, and/or sell copies
+ * of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be
+ * included in all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+ * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
+ * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
+ * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
+ * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+ * SOFTWARE.
+ */
+
+#import "NSDictionary+DDHidExtras.h"
+
+
+@implementation NSDictionary (DDHidExtras)
+
+- (unsigned) ddhid_unsignedForKey: (NSString *) key;
+{
+ NSNumber * number = [self objectForKey: key];
+ return [number unsignedIntValue];
+}
+
+- (id) ddhid_objectForString: (const char *) key;
+{
+ NSString * objcKey = [NSString stringWithUTF8String: key];
+ return [self objectForKey: objcKey];
+}
+
+- (NSString *) ddhid_stringForString: (const char *) key;
+{
+ return [self ddhid_objectForString: key];
+}
+
+- (long) ddhid_longForString: (const char *) key;
+{
+ NSNumber * number = [self ddhid_objectForString: key];
+ return [number longValue];
+}
+
+- (unsigned int) ddhid_unsignedIntForString: (const char *) key;
+{
+ NSNumber * number = [self ddhid_objectForString: key];
+ return [number unsignedIntValue];
+}
+
+- (BOOL) ddhid_boolForString: (const char *) key;
+{
+ NSNumber * number = [self ddhid_objectForString: key];
+ return [number boolValue];
+}
+
+@end
+
+@implementation NSMutableDictionary (DDHidExtras)
+
+- (void) ddhid_setObject: (id) object forString: (const char *) key;
+{
+ NSString * objcKey = [NSString stringWithUTF8String: key];
+ [self setObject: object forKey: objcKey];
+}
+
+- (void) ddhid_setInt: (int) i forKey: (id) key;
+{
+ NSNumber * number = [NSNumber numberWithInt: i];
+ [self setObject: number forKey: key];
+}
+
+@end