1691 lines
62 KiB
C
1691 lines
62 KiB
C
/*
|
|
Copyright (c) 2003-2007 Ryan C. Gordon and others.
|
|
|
|
http://icculus.org/manymouse/
|
|
|
|
This software is provided 'as-is', without any express or implied warranty.
|
|
In no event will the authors be held liable for any damages arising from
|
|
the use of this software.
|
|
|
|
Permission is granted to anyone to use this software for any purpose,
|
|
including commercial applications, and to alter it and redistribute it
|
|
freely, subject to the following restrictions:
|
|
|
|
1. The origin of this software must not be misrepresented; you must not
|
|
claim that you wrote the original software. If you use this software in a
|
|
product, an acknowledgment in the product documentation would be
|
|
appreciated but is not required.
|
|
|
|
2. Altered source versions must be plainly marked as such, and must not be
|
|
misrepresented as being the original software.
|
|
|
|
3. This notice may not be removed or altered from any source distribution.
|
|
|
|
Ryan C. Gordon <icculus@icculus.org>
|
|
*/
|
|
|
|
//Support for MacOS X via the HID Manager APIs.
|
|
|
|
#include "mm.h"
|
|
|
|
#if ( (defined(__MACH__)) && (defined(__APPLE__)) )
|
|
|
|
/*
|
|
* This source is almost entirely lifted from Apple's HID Utilities
|
|
* example source code, written by George Warner:
|
|
*
|
|
* http://developer.apple.com/samplecode/HID_Utilities_Source/HID_Utilities_Source.html
|
|
*
|
|
* The source license to HID Utilities allows this sort of blatant stealing.
|
|
*
|
|
* Patches to HID Utilities have comments like "ryan added this", otherwise,
|
|
* I just tried to cut down that package to the smallest set of functions
|
|
* I needed.
|
|
*
|
|
* Scroll down for "-- END HID UTILITIES --" to see the ManyMouse glue code.
|
|
*/
|
|
|
|
#include <Carbon/Carbon.h>
|
|
|
|
#include <IOKit/IOTypes.h>
|
|
// 10.0.x
|
|
//#include <IOKit/IOUSBHIDParser.h>
|
|
// 10.1.x
|
|
#include <IOKit/hid/IOHIDUsageTables.h>
|
|
#include <IOKit/hid/IOHIDLib.h>
|
|
#include <IOKit/IOCFPlugIn.h>
|
|
#include <IOKit/IOKitLib.h>
|
|
#include <IOKit/IOMessage.h>
|
|
|
|
#define USE_NOTIFICATIONS 1
|
|
|
|
#define HIDREPORTERRORNUM(s,n) do {} while (false)
|
|
#define HIDREPORTERROR(s) do {} while (false)
|
|
|
|
typedef enum HIDElementTypeMask
|
|
{
|
|
kHIDElementTypeInput = 1 << 1,
|
|
kHIDElementTypeOutput = 1 << 2,
|
|
kHIDElementTypeFeature = 1 << 3,
|
|
kHIDElementTypeCollection = 1 << 4,
|
|
kHIDElementTypeIO = kHIDElementTypeInput | kHIDElementTypeOutput | kHIDElementTypeFeature,
|
|
kHIDElementTypeAll = kHIDElementTypeIO | kHIDElementTypeCollection
|
|
}HIDElementTypeMask;
|
|
|
|
enum
|
|
{
|
|
kDefaultUserMin = 0, // default user min and max used for scaling
|
|
kDefaultUserMax = 255
|
|
};
|
|
|
|
enum
|
|
{
|
|
kDeviceQueueSize = 50 // this is wired kernel memory so should be set to as small as possible
|
|
// but should account for the maximum possible events in the queue
|
|
// USB updates will likely occur at 100 Hz so one must account for this rate of
|
|
// if states change quickly (updates are only posted on state changes)
|
|
};
|
|
|
|
struct recElement
|
|
{
|
|
unsigned long type; // the type defined by IOHIDElementType in IOHIDKeys.h
|
|
long usagePage; // usage page from IOUSBHIDParser.h which defines general usage
|
|
long usage; // usage within above page from IOUSBHIDParser.h which defines specific usage
|
|
void * cookie; // unique value (within device of specific vendorID and productID) which identifies element, will NOT change
|
|
long min; // reported min value possible
|
|
long max; // reported max value possible
|
|
long scaledMin; // reported scaled min value possible
|
|
long scaledMax; // reported scaled max value possible
|
|
long size; // size in bits of data return from element
|
|
unsigned char relative; // are reports relative to last report (deltas)
|
|
unsigned char wrapping; // does element wrap around (one value higher than max is min)
|
|
unsigned char nonLinear; // are the values reported non-linear relative to element movement
|
|
unsigned char preferredState; // does element have a preferred state (such as a button)
|
|
unsigned char nullState; // does element have null state
|
|
long units; // units value is reported in (not used very often)
|
|
long unitExp; // exponent for units (also not used very often)
|
|
char name[256]; // name of element (c string)
|
|
|
|
// runtime variables
|
|
long calMin; // min returned value
|
|
long calMax; // max returned value (calibrate call)
|
|
long userMin; // user set value to scale to (scale call)
|
|
long userMax;
|
|
|
|
struct recElement * pPrevious; // previous element (NULL at list head)
|
|
struct recElement * pChild; // next child (only of collections)
|
|
struct recElement * pSibling; // next sibling (for elements and collections)
|
|
|
|
long depth;
|
|
};
|
|
typedef struct recElement recElement;
|
|
typedef recElement* pRecElement;
|
|
|
|
// ryan added this.
|
|
typedef enum
|
|
{
|
|
DISCONNECT_CONNECTED,
|
|
DISCONNECT_TELLUSER,
|
|
DISCONNECT_COMPLETE
|
|
} DisconnectState;
|
|
|
|
struct recDevice
|
|
{
|
|
void * interface; // interface to device, NULL = no interface
|
|
void * queue; // device queue, NULL = no queue
|
|
void * queueRunLoopSource; // device queue run loop source, NULL == no source
|
|
void * transaction; // output transaction interface, NULL == no interface
|
|
void * notification; // notifications
|
|
char transport[256]; // device transport (c string)
|
|
long vendorID; // id for device vendor, unique across all devices
|
|
long productID; // id for particular product, unique across all of a vendors devices
|
|
long version; // version of product
|
|
char manufacturer[256]; // name of manufacturer
|
|
char product[256]; // name of product
|
|
char serial[256]; // serial number of specific product, can be assumed unique across specific product or specific vendor (not used often)
|
|
long locID; // long representing location in USB (or other I/O) chain which device is pluged into, can identify specific device on machine
|
|
long usage; // usage page from IOUSBHID Parser.h which defines general usage
|
|
long usagePage; // usage within above page from IOUSBHID Parser.h which defines specific usage
|
|
long totalElements; // number of total elements (should be total of all elements on device including collections) (calculated, not reported by device)
|
|
long features; // number of elements of type kIOHIDElementTypeFeature
|
|
long inputs; // number of elements of type kIOHIDElementTypeInput_Misc or kIOHIDElementTypeInput_Button or kIOHIDElementTypeInput_Axis or kIOHIDElementTypeInput_ScanCodes
|
|
long outputs; // number of elements of type kIOHIDElementTypeOutput
|
|
long collections; // number of elements of type kIOHIDElementTypeCollection
|
|
long axis; // number of axis (calculated, not reported by device)
|
|
long buttons; // number of buttons (calculated, not reported by device)
|
|
long hats; // number of hat switches (calculated, not reported by device)
|
|
long sliders; // number of sliders (calculated, not reported by device)
|
|
long dials; // number of dials (calculated, not reported by device)
|
|
long wheels; // number of wheels (calculated, not reported by device)
|
|
recElement* pListElements; // head of linked list of elements
|
|
DisconnectState disconnect; // (ryan added this.)
|
|
AbsoluteTime lastScrollTime; // (ryan added this.)
|
|
struct recDevice* pNext; // next device
|
|
};
|
|
typedef struct recDevice recDevice;
|
|
typedef recDevice* pRecDevice;
|
|
|
|
|
|
#if USE_NOTIFICATIONS
|
|
static IONotificationPortRef gNotifyPort;
|
|
static io_iterator_t gAddedIter;
|
|
static CFRunLoopRef gRunLoop;
|
|
#endif USE_NOTIFICATIONS
|
|
|
|
// for element retrieval
|
|
static pRecDevice gCurrentGetDevice = NULL;
|
|
static Boolean gAddAsChild = false;
|
|
static int gDepth = false;
|
|
|
|
static pRecDevice gpDeviceList = NULL;
|
|
static UInt32 gNumDevices = 0;
|
|
|
|
static Boolean HIDIsValidDevice(const pRecDevice pSearchDevice);
|
|
static pRecElement HIDGetFirstDeviceElement (pRecDevice pDevice, HIDElementTypeMask typeMask);
|
|
static pRecElement HIDGetNextDeviceElement (pRecElement pElement, HIDElementTypeMask typeMask);
|
|
static pRecDevice HIDGetFirstDevice (void);
|
|
static pRecDevice HIDGetNextDevice (pRecDevice pDevice);
|
|
static void HIDReleaseDeviceList (void);
|
|
static unsigned long HIDDequeueDevice (pRecDevice pDevice);
|
|
static void hid_GetElements (CFTypeRef refElementCurrent, pRecElement *ppCurrentElement);
|
|
|
|
|
|
static void HIDReportError(const char *err) {}
|
|
static void HIDReportErrorNum(const char *err, int num) {}
|
|
|
|
|
|
static void hid_GetCollectionElements (CFMutableDictionaryRef deviceProperties, pRecElement *ppCurrentCollection)
|
|
{
|
|
CFTypeRef refElementTop = CFDictionaryGetValue (deviceProperties, CFSTR(kIOHIDElementKey));
|
|
if (refElementTop)
|
|
hid_GetElements (refElementTop, ppCurrentCollection);
|
|
else
|
|
HIDReportError ("hid_GetCollectionElements: CFDictionaryGetValue error when creating CFTypeRef for kIOHIDElementKey.");
|
|
}
|
|
|
|
|
|
// extracts actual specific element information from each element CF dictionary entry
|
|
static void hid_GetElementInfo (CFTypeRef refElement, pRecElement pElement)
|
|
{
|
|
long number;
|
|
CFTypeRef refType;
|
|
// type, usagePage, usage already stored
|
|
refType = CFDictionaryGetValue (refElement, CFSTR(kIOHIDElementCookieKey));
|
|
if (refType && CFNumberGetValue (refType, kCFNumberLongType, &number))
|
|
pElement->cookie = (IOHIDElementCookie) number;
|
|
else
|
|
pElement->cookie = (IOHIDElementCookie) 0;
|
|
|
|
refType = CFDictionaryGetValue (refElement, CFSTR(kIOHIDElementMinKey));
|
|
if (refType && CFNumberGetValue (refType, kCFNumberLongType, &number))
|
|
pElement->min = number;
|
|
else
|
|
pElement->min = 0;
|
|
|
|
pElement->calMax = pElement->min;
|
|
pElement->userMin = kDefaultUserMin;
|
|
|
|
refType = CFDictionaryGetValue (refElement, CFSTR(kIOHIDElementMaxKey));
|
|
if (refType && CFNumberGetValue (refType, kCFNumberLongType, &number))
|
|
pElement->max = number;
|
|
else
|
|
pElement->max = 0;
|
|
|
|
pElement->calMin = pElement->max;
|
|
pElement->userMax = kDefaultUserMax;
|
|
|
|
refType = CFDictionaryGetValue (refElement, CFSTR(kIOHIDElementScaledMinKey));
|
|
if (refType && CFNumberGetValue (refType, kCFNumberLongType, &number))
|
|
pElement->scaledMin = number;
|
|
else
|
|
pElement->scaledMin = 0;
|
|
|
|
refType = CFDictionaryGetValue (refElement, CFSTR(kIOHIDElementScaledMaxKey));
|
|
if (refType && CFNumberGetValue (refType, kCFNumberLongType, &number))
|
|
pElement->scaledMax = number;
|
|
else
|
|
pElement->scaledMax = 0;
|
|
|
|
refType = CFDictionaryGetValue (refElement, CFSTR(kIOHIDElementSizeKey));
|
|
if (refType && CFNumberGetValue (refType, kCFNumberLongType, &number))
|
|
pElement->size = number;
|
|
else
|
|
pElement->size = 0;
|
|
|
|
refType = CFDictionaryGetValue (refElement, CFSTR(kIOHIDElementIsRelativeKey));
|
|
if (refType)
|
|
pElement->relative = CFBooleanGetValue (refType);
|
|
else
|
|
pElement->relative = 0;
|
|
|
|
refType = CFDictionaryGetValue (refElement, CFSTR(kIOHIDElementIsWrappingKey));
|
|
if (refType)
|
|
pElement->wrapping = CFBooleanGetValue (refType);
|
|
else
|
|
pElement->wrapping = false;
|
|
|
|
refType = CFDictionaryGetValue (refElement, CFSTR(kIOHIDElementIsNonLinearKey));
|
|
if (refType)
|
|
pElement->nonLinear = CFBooleanGetValue (refType);
|
|
else
|
|
pElement->wrapping = false;
|
|
|
|
#ifdef kIOHIDElementHasPreferredStateKey
|
|
refType = CFDictionaryGetValue (refElement, CFSTR(kIOHIDElementHasPreferredStateKey));
|
|
#else // Mac OS X 10.0 has spelling error
|
|
refType = CFDictionaryGetValue (refElement, CFSTR(kIOHIDElementHasPreferedStateKey));
|
|
#endif
|
|
if (refType)
|
|
pElement->preferredState = CFBooleanGetValue (refType);
|
|
else
|
|
pElement->preferredState = false;
|
|
|
|
refType = CFDictionaryGetValue (refElement, CFSTR(kIOHIDElementHasNullStateKey));
|
|
if (refType)
|
|
pElement->nullState = CFBooleanGetValue (refType);
|
|
else
|
|
pElement->nullState = false;
|
|
|
|
refType = CFDictionaryGetValue (refElement, CFSTR(kIOHIDElementUnitKey));
|
|
if (refType && CFNumberGetValue (refType, kCFNumberLongType, &number))
|
|
pElement->units = number;
|
|
else
|
|
pElement->units = 0;
|
|
|
|
refType = CFDictionaryGetValue (refElement, CFSTR(kIOHIDElementUnitExponentKey));
|
|
if (refType && CFNumberGetValue (refType, kCFNumberLongType, &number))
|
|
pElement->unitExp = number;
|
|
else
|
|
pElement->unitExp = 0;
|
|
|
|
refType = CFDictionaryGetValue (refElement, CFSTR(kIOHIDElementNameKey));
|
|
if (refType)
|
|
if (!CFStringGetCString (refType, pElement->name, 256, CFStringGetSystemEncoding ()))
|
|
HIDReportError ("CFStringGetCString error retrieving pElement->name.");
|
|
|
|
#if 0
|
|
if (!*pElement->name)
|
|
{
|
|
// set name from vendor id, product id & usage info look up
|
|
if (!HIDGetElementNameFromVendorProductUsage (gCurrentGetDevice->vendorID, gCurrentGetDevice->productID, pElement->usagePage, pElement->usage, pElement->name))
|
|
{
|
|
// set name from vendor id/product id look up
|
|
HIDGetElementNameFromVendorProductCookie (gCurrentGetDevice->vendorID, gCurrentGetDevice->productID, (long) pElement->cookie, pElement->name);
|
|
if (!*pElement->name) { // if no name
|
|
HIDGetUsageName (pElement->usagePage, pElement->usage, pElement->name);
|
|
if (!*pElement->name) // if not usage
|
|
sprintf (pElement->name, "Element");
|
|
}
|
|
}
|
|
}
|
|
#endif
|
|
}
|
|
|
|
|
|
static void hid_AddElement (CFTypeRef refElement, pRecElement * ppElementCurrent)
|
|
{
|
|
pRecDevice pDevice = gCurrentGetDevice;
|
|
pRecElement pElement = NULL;
|
|
long elementType, usagePage, usage;
|
|
CFTypeRef refElementType = CFDictionaryGetValue (refElement, CFSTR(kIOHIDElementTypeKey));
|
|
CFTypeRef refUsagePage = CFDictionaryGetValue (refElement, CFSTR(kIOHIDElementUsagePageKey));
|
|
CFTypeRef refUsage = CFDictionaryGetValue (refElement, CFSTR(kIOHIDElementUsageKey));
|
|
|
|
if (refElementType)
|
|
CFNumberGetValue (refElementType, kCFNumberLongType, &elementType);
|
|
if (refUsagePage)
|
|
CFNumberGetValue (refUsagePage, kCFNumberLongType, &usagePage);
|
|
if (refUsage)
|
|
CFNumberGetValue (refUsage, kCFNumberLongType, &usage);
|
|
|
|
if (NULL == pDevice)
|
|
return;
|
|
|
|
if (elementType)
|
|
{
|
|
// look at types of interest
|
|
if (elementType != kIOHIDElementTypeCollection)
|
|
{
|
|
if (usagePage && usage) // if valid usage and page
|
|
{
|
|
switch (usagePage) // only interested in kHIDPage_GenericDesktop and kHIDPage_Button
|
|
{
|
|
case kHIDPage_GenericDesktop:
|
|
{
|
|
switch (usage) // look at usage to determine function
|
|
{
|
|
case kHIDUsage_GD_X:
|
|
case kHIDUsage_GD_Y:
|
|
case kHIDUsage_GD_Z:
|
|
case kHIDUsage_GD_Rx:
|
|
case kHIDUsage_GD_Ry:
|
|
case kHIDUsage_GD_Rz:
|
|
pElement = (pRecElement) malloc (sizeof (recElement));
|
|
if (pElement) pDevice->axis++;
|
|
break;
|
|
case kHIDUsage_GD_Slider:
|
|
pElement = (pRecElement) malloc (sizeof (recElement));
|
|
if (pElement) pDevice->sliders++;
|
|
break;
|
|
case kHIDUsage_GD_Dial:
|
|
pElement = (pRecElement) malloc (sizeof (recElement));
|
|
if (pElement) pDevice->dials++;
|
|
break;
|
|
case kHIDUsage_GD_Wheel:
|
|
pElement = (pRecElement) malloc (sizeof (recElement));
|
|
if (pElement) pDevice->wheels++;
|
|
break;
|
|
case kHIDUsage_GD_Hatswitch:
|
|
pElement = (pRecElement) malloc (sizeof (recElement));
|
|
if (pElement) pDevice->hats++;
|
|
break;
|
|
default:
|
|
pElement = (pRecElement) malloc (sizeof (recElement));
|
|
break;
|
|
}
|
|
}
|
|
break;
|
|
case kHIDPage_Button:
|
|
pElement = (pRecElement) malloc (sizeof (recElement));
|
|
if (pElement) pDevice->buttons++;
|
|
break;
|
|
default:
|
|
// just add a generic element
|
|
pElement = (pRecElement) malloc (sizeof (recElement));
|
|
break;
|
|
}
|
|
}
|
|
#if 0
|
|
else
|
|
HIDReportError ("CFNumberGetValue error when getting value for refUsage or refUsagePage.");
|
|
#endif 0
|
|
}
|
|
else // collection
|
|
pElement = (pRecElement) malloc (sizeof (recElement));
|
|
}
|
|
else
|
|
HIDReportError ("CFNumberGetValue error when getting value for refElementType.");
|
|
|
|
if (pElement) // add to list
|
|
{
|
|
// this code builds a binary tree based on the collection hierarchy of inherent in the device element layout
|
|
// it preserves the structure of the lements as collections have children and elements are siblings to each other
|
|
|
|
// clear record
|
|
bzero(pElement,sizeof(recElement));
|
|
|
|
// get element info
|
|
pElement->type = elementType;
|
|
pElement->usagePage = usagePage;
|
|
pElement->usage = usage;
|
|
pElement->depth = 0; // assume root object
|
|
hid_GetElementInfo (refElement, pElement);
|
|
|
|
// count elements
|
|
pDevice->totalElements++;
|
|
|
|
switch (pElement->type)
|
|
{
|
|
case kIOHIDElementTypeInput_Misc:
|
|
case kIOHIDElementTypeInput_Button:
|
|
case kIOHIDElementTypeInput_Axis:
|
|
case kIOHIDElementTypeInput_ScanCodes:
|
|
pDevice->inputs++;
|
|
break;
|
|
case kIOHIDElementTypeOutput:
|
|
pDevice->outputs++;
|
|
break;
|
|
case kIOHIDElementTypeFeature:
|
|
pDevice->features++;
|
|
break;
|
|
case kIOHIDElementTypeCollection:
|
|
pDevice->collections++;
|
|
break;
|
|
default:
|
|
HIDReportErrorNum ("Unknown element type : ", pElement->type);
|
|
}
|
|
|
|
if (NULL == *ppElementCurrent) // if at list head
|
|
{
|
|
pDevice->pListElements = pElement; // add current element
|
|
*ppElementCurrent = pElement; // set current element to element we just added
|
|
}
|
|
else // have exsiting structure
|
|
{
|
|
if (gAddAsChild) // if the previous element was a collection, let's add this as a child of the previous
|
|
{
|
|
// this iteration should not be needed but there maybe some untested degenerate case which this code will ensure works
|
|
while ((*ppElementCurrent)->pChild) // step down tree until free child node found
|
|
*ppElementCurrent = (*ppElementCurrent)->pChild;
|
|
(*ppElementCurrent)->pChild = pElement; // insert there
|
|
pElement->depth = (*ppElementCurrent)->depth + 1;
|
|
}
|
|
else // add as sibling
|
|
{
|
|
// this iteration should not be needed but there maybe some untested degenerate case which this code will ensure works
|
|
while ((*ppElementCurrent)->pSibling) // step down tree until free sibling node found
|
|
*ppElementCurrent = (*ppElementCurrent)->pSibling;
|
|
(*ppElementCurrent)->pSibling = pElement; // insert there
|
|
pElement->depth = (*ppElementCurrent)->depth;
|
|
}
|
|
pElement->pPrevious = *ppElementCurrent; // point to previous
|
|
*ppElementCurrent = pElement; // set current to our collection
|
|
}
|
|
|
|
if (elementType == kIOHIDElementTypeCollection) // if this element is a collection of other elements
|
|
{
|
|
gAddAsChild = true; // add next set as children to this element
|
|
gDepth++;
|
|
hid_GetCollectionElements ((CFMutableDictionaryRef) refElement, &pElement); // recursively process the collection
|
|
gDepth--;
|
|
}
|
|
gAddAsChild = false; // add next as this elements sibling (when return from a collection or with non-collections)
|
|
}
|
|
#if 0
|
|
else
|
|
HIDReportError ("hid_AddElement - no element added.");
|
|
#endif
|
|
}
|
|
|
|
|
|
static void hid_GetElementsCFArrayHandler (const void * value, void * parameter)
|
|
{
|
|
if (CFGetTypeID (value) == CFDictionaryGetTypeID ())
|
|
hid_AddElement ((CFTypeRef) value, (pRecElement *) parameter);
|
|
}
|
|
|
|
// ---------------------------------
|
|
// handles retrieval of element information from arrays of elements in device IO registry information
|
|
|
|
static void hid_GetElements (CFTypeRef refElementCurrent, pRecElement *ppCurrentElement)
|
|
{
|
|
CFTypeID type = CFGetTypeID (refElementCurrent);
|
|
if (type == CFArrayGetTypeID()) // if element is an array
|
|
{
|
|
CFRange range = {0, CFArrayGetCount (refElementCurrent)};
|
|
// CountElementsCFArrayHandler called for each array member
|
|
CFArrayApplyFunction (refElementCurrent, range, hid_GetElementsCFArrayHandler, ppCurrentElement);
|
|
}
|
|
}
|
|
|
|
static void hid_TopLevelElementHandler (const void * value, void * parameter)
|
|
{
|
|
CFTypeRef refCF = 0;
|
|
if ((NULL == value) || (NULL == parameter))
|
|
return; // (kIOReturnBadArgument)
|
|
if (CFGetTypeID (value) != CFDictionaryGetTypeID ())
|
|
return; // (kIOReturnBadArgument)
|
|
refCF = CFDictionaryGetValue (value, CFSTR(kIOHIDElementUsagePageKey));
|
|
if (!CFNumberGetValue (refCF, kCFNumberLongType, &((pRecDevice) parameter)->usagePage))
|
|
HIDReportError ("CFNumberGetValue error retrieving pDevice->usagePage.");
|
|
refCF = CFDictionaryGetValue (value, CFSTR(kIOHIDElementUsageKey));
|
|
if (!CFNumberGetValue (refCF, kCFNumberLongType, &((pRecDevice) parameter)->usage))
|
|
HIDReportError ("CFNumberGetValue error retrieving pDevice->usage.");
|
|
}
|
|
|
|
|
|
static void hid_GetDeviceInfo (io_object_t hidDevice, CFMutableDictionaryRef hidProperties, pRecDevice pDevice)
|
|
{
|
|
CFMutableDictionaryRef usbProperties = 0;
|
|
io_registry_entry_t parent1, parent2;
|
|
|
|
// Mac OS X currently is not mirroring all USB properties to HID page so need to look at USB device page also
|
|
// get dictionary for usb properties: step up two levels and get CF dictionary for USB properties
|
|
if ((KERN_SUCCESS == IORegistryEntryGetParentEntry (hidDevice, kIOServicePlane, &parent1)) &&
|
|
(KERN_SUCCESS == IORegistryEntryGetParentEntry (parent1, kIOServicePlane, &parent2)) &&
|
|
(KERN_SUCCESS == IORegistryEntryCreateCFProperties (parent2, &usbProperties, kCFAllocatorDefault, kNilOptions)))
|
|
{
|
|
if (usbProperties)
|
|
{
|
|
CFTypeRef refCF = 0;
|
|
// get device info
|
|
// try hid dictionary first, if fail then go to usb dictionary
|
|
|
|
// get transport
|
|
refCF = CFDictionaryGetValue (hidProperties, CFSTR(kIOHIDTransportKey));
|
|
if (refCF)
|
|
{
|
|
if (!CFStringGetCString (refCF, pDevice->transport, 256, CFStringGetSystemEncoding ()))
|
|
HIDReportError ("CFStringGetCString error retrieving pDevice->transport.");
|
|
}
|
|
|
|
// get vendorID
|
|
refCF = CFDictionaryGetValue (hidProperties, CFSTR(kIOHIDVendorIDKey));
|
|
if (!refCF)
|
|
refCF = CFDictionaryGetValue (usbProperties, CFSTR("idVendor"));
|
|
if (refCF)
|
|
{
|
|
if (!CFNumberGetValue (refCF, kCFNumberLongType, &pDevice->vendorID))
|
|
HIDReportError ("CFNumberGetValue error retrieving pDevice->vendorID.");
|
|
}
|
|
|
|
// get product ID
|
|
refCF = CFDictionaryGetValue (hidProperties, CFSTR(kIOHIDProductIDKey));
|
|
if (!refCF)
|
|
refCF = CFDictionaryGetValue (usbProperties, CFSTR("idProduct"));
|
|
if (refCF)
|
|
{
|
|
if (!CFNumberGetValue (refCF, kCFNumberLongType, &pDevice->productID))
|
|
HIDReportError ("CFNumberGetValue error retrieving pDevice->productID.");
|
|
}
|
|
|
|
// get product version
|
|
refCF = CFDictionaryGetValue (hidProperties, CFSTR(kIOHIDVersionNumberKey));
|
|
if (refCF)
|
|
{
|
|
if (!CFNumberGetValue (refCF, kCFNumberLongType, &pDevice->version))
|
|
HIDReportError ("CFNumberGetValue error retrieving pDevice->version.");
|
|
}
|
|
|
|
// get manufacturer name
|
|
refCF = CFDictionaryGetValue (hidProperties, CFSTR(kIOHIDManufacturerKey));
|
|
if (!refCF)
|
|
refCF = CFDictionaryGetValue (usbProperties, CFSTR("USB Vendor Name"));
|
|
if (refCF)
|
|
{
|
|
if (!CFStringGetCString (refCF, pDevice->manufacturer, 256, CFStringGetSystemEncoding ()))
|
|
HIDReportError ("CFStringGetCString error retrieving pDevice->manufacturer.");
|
|
}
|
|
|
|
// get product name
|
|
refCF = CFDictionaryGetValue (hidProperties, CFSTR(kIOHIDProductKey));
|
|
if (!refCF)
|
|
refCF = CFDictionaryGetValue (usbProperties, CFSTR("USB Product Name"));
|
|
if (refCF)
|
|
{
|
|
if (!CFStringGetCString (refCF, pDevice->product, 256, CFStringGetSystemEncoding ()))
|
|
HIDReportError ("CFStringGetCString error retrieving pDevice->product.");
|
|
}
|
|
|
|
// get serial
|
|
refCF = CFDictionaryGetValue (hidProperties, CFSTR(kIOHIDSerialNumberKey));
|
|
if (refCF)
|
|
{
|
|
if (!CFStringGetCString (refCF, pDevice->serial, 256, CFStringGetSystemEncoding ()))
|
|
HIDReportError ("CFStringGetCString error retrieving pDevice->serial.");
|
|
}
|
|
|
|
// get location ID
|
|
refCF = CFDictionaryGetValue (hidProperties, CFSTR(kIOHIDLocationIDKey));
|
|
if (!refCF)
|
|
refCF = CFDictionaryGetValue (usbProperties, CFSTR("locationID"));
|
|
if (refCF)
|
|
{
|
|
if (!CFNumberGetValue (refCF, kCFNumberLongType, &pDevice->locID))
|
|
HIDReportError ("CFNumberGetValue error retrieving pDevice->locID.");
|
|
}
|
|
|
|
// get usage page and usage
|
|
refCF = CFDictionaryGetValue (hidProperties, CFSTR(kIOHIDPrimaryUsagePageKey));
|
|
if (refCF)
|
|
{
|
|
if (!CFNumberGetValue (refCF, kCFNumberLongType, &pDevice->usagePage))
|
|
HIDReportError ("CFNumberGetValue error retrieving pDevice->usagePage.");
|
|
refCF = CFDictionaryGetValue (hidProperties, CFSTR(kIOHIDPrimaryUsageKey));
|
|
if (refCF)
|
|
if (!CFNumberGetValue (refCF, kCFNumberLongType, &pDevice->usage))
|
|
HIDReportError ("CFNumberGetValue error retrieving pDevice->usage.");
|
|
}
|
|
if (NULL == refCF) // get top level element HID usage page or usage
|
|
{
|
|
// use top level element instead
|
|
CFTypeRef refCFTopElement = 0;
|
|
refCFTopElement = CFDictionaryGetValue (hidProperties, CFSTR(kIOHIDElementKey));
|
|
{
|
|
// refCFTopElement points to an array of element dictionaries
|
|
CFRange range = {0, CFArrayGetCount (refCFTopElement)};
|
|
CFArrayApplyFunction (refCFTopElement, range, hid_TopLevelElementHandler, NULL);
|
|
}
|
|
}
|
|
}
|
|
else
|
|
HIDReportError ("IORegistryEntryCreateCFProperties failed to create usbProperties.");
|
|
|
|
CFRelease (usbProperties);
|
|
if (kIOReturnSuccess != IOObjectRelease (parent2))
|
|
HIDReportError ("IOObjectRelease error with parent2.");
|
|
if (kIOReturnSuccess != IOObjectRelease (parent1))
|
|
HIDReportError ("IOObjectRelease error with parent1.");
|
|
}
|
|
}
|
|
|
|
|
|
static Boolean hid_MatchElementTypeMask (IOHIDElementType type, HIDElementTypeMask typeMask)
|
|
{
|
|
if (typeMask & kHIDElementTypeInput)
|
|
if ((type == kIOHIDElementTypeInput_Misc) || (type == kIOHIDElementTypeInput_Button) || (type == kIOHIDElementTypeInput_Axis) || (type == kIOHIDElementTypeInput_ScanCodes))
|
|
return true;
|
|
if (typeMask & kHIDElementTypeOutput)
|
|
if (type == kIOHIDElementTypeOutput)
|
|
return true;
|
|
if (typeMask & kHIDElementTypeFeature)
|
|
if (type == kIOHIDElementTypeFeature)
|
|
return true;
|
|
if (typeMask & kHIDElementTypeCollection)
|
|
if (type == kIOHIDElementTypeCollection)
|
|
return true;
|
|
return false;
|
|
}
|
|
|
|
static pRecElement hid_GetDeviceElement (pRecElement pElement, HIDElementTypeMask typeMask)
|
|
{
|
|
// we are asking for this element
|
|
if (NULL != pElement)
|
|
{
|
|
if (hid_MatchElementTypeMask (pElement->type, typeMask)) // if the type match what we are looking for
|
|
return pElement; // return the element
|
|
else
|
|
return HIDGetNextDeviceElement (pElement, typeMask); // else get the next one
|
|
}
|
|
return NULL;
|
|
}
|
|
|
|
static unsigned long HIDCloseReleaseInterface (pRecDevice pDevice)
|
|
{
|
|
IOReturn result = kIOReturnSuccess;
|
|
|
|
if (HIDIsValidDevice(pDevice) && (NULL != pDevice->interface))
|
|
{
|
|
// close the interface
|
|
result = (*(IOHIDDeviceInterface**) pDevice->interface)->close (pDevice->interface);
|
|
if (kIOReturnNotOpen == result)
|
|
{
|
|
// do nothing as device was not opened, thus can't be closed
|
|
}
|
|
else if (kIOReturnSuccess != result)
|
|
HIDREPORTERRORNUM ("HIDCloseReleaseInterface - Failed to close IOHIDDeviceInterface.", result);
|
|
//release the interface
|
|
result = (*(IOHIDDeviceInterface**) pDevice->interface)->Release (pDevice->interface);
|
|
if (kIOReturnSuccess != result)
|
|
HIDREPORTERRORNUM ("HIDCloseReleaseInterface - Failed to release interface.", result);
|
|
pDevice->interface = NULL;
|
|
}
|
|
return result;
|
|
}
|
|
|
|
|
|
// ---------------------------------
|
|
// count number of devices in global device list (gpDeviceList)
|
|
static UInt32 hid_CountCurrentDevices (void)
|
|
{
|
|
pRecDevice pDevice = gpDeviceList;
|
|
UInt32 devices = 0;
|
|
while (pDevice)
|
|
{
|
|
devices++;
|
|
pDevice = pDevice->pNext;
|
|
}
|
|
return devices;
|
|
}
|
|
|
|
static UInt32 HIDCountDevices (void)
|
|
{
|
|
gNumDevices = hid_CountCurrentDevices ();
|
|
|
|
return gNumDevices;
|
|
}
|
|
|
|
static void hid_DisposeDeviceElements (pRecElement pElement)
|
|
{
|
|
if (pElement)
|
|
{
|
|
if (pElement->pChild)
|
|
hid_DisposeDeviceElements (pElement->pChild);
|
|
if (pElement->pSibling)
|
|
hid_DisposeDeviceElements (pElement->pSibling);
|
|
free (pElement);
|
|
}
|
|
}
|
|
|
|
static pRecDevice hid_DisposeDevice (pRecDevice pDevice)
|
|
{
|
|
kern_return_t result = KERN_SUCCESS;
|
|
pRecDevice pDeviceNext = NULL;
|
|
|
|
if (HIDIsValidDevice(pDevice))
|
|
{
|
|
// save next device prior to disposing of this device
|
|
pDeviceNext = pDevice->pNext;
|
|
|
|
result = HIDDequeueDevice (pDevice);
|
|
#if 0
|
|
if (kIOReturnSuccess != result)
|
|
HIDReportErrorNum ("hid_DisposeDevice: HIDDequeueDevice error: 0x%8.8X.", result);
|
|
#endif 1
|
|
|
|
hid_DisposeDeviceElements (pDevice->pListElements);
|
|
pDevice->pListElements = NULL;
|
|
|
|
result = HIDCloseReleaseInterface (pDevice); // function sanity checks interface value (now application does not own device)
|
|
if (kIOReturnSuccess != result)
|
|
HIDReportErrorNum ("hid_DisposeDevice: HIDCloseReleaseInterface error: 0x%8.8X.", result);
|
|
|
|
#if USE_NOTIFICATIONS
|
|
if (pDevice->interface)
|
|
{
|
|
// replace (*pDevice->interface)->Release(pDevice->interface);
|
|
result = IODestroyPlugInInterface (pDevice->interface);
|
|
if (kIOReturnSuccess != result)
|
|
HIDReportErrorNum ("hid_DisposeDevice: IODestroyPlugInInterface error: 0x%8.8X.", result);
|
|
}
|
|
|
|
if (pDevice->notification)
|
|
{
|
|
result = IOObjectRelease((io_object_t) pDevice->notification);
|
|
if (kIOReturnSuccess != result)
|
|
HIDReportErrorNum ("hid_DisposeDevice: IOObjectRelease error: 0x%8.8X.", result);
|
|
}
|
|
#endif USE_NOTIFICATIONS
|
|
|
|
// remove this device from the device list
|
|
if (gpDeviceList == pDevice) // head of list?
|
|
gpDeviceList = pDeviceNext;
|
|
else
|
|
{
|
|
pRecDevice pDeviceTemp = pDeviceNext = gpDeviceList; // we're going to return this if we don't find ourselfs in the list
|
|
while (pDeviceTemp)
|
|
{
|
|
if (pDeviceTemp->pNext == pDevice) // found us!
|
|
{
|
|
// take us out of linked list
|
|
pDeviceTemp->pNext = pDeviceNext = pDevice->pNext;
|
|
break;
|
|
}
|
|
pDeviceTemp = pDeviceTemp->pNext;
|
|
}
|
|
}
|
|
free (pDevice);
|
|
}
|
|
|
|
// update device count
|
|
gNumDevices = hid_CountCurrentDevices ();
|
|
|
|
return pDeviceNext;
|
|
}
|
|
|
|
|
|
// ---------------------------------
|
|
// disposes and releases queue, sets queue to NULL,.
|
|
// Note: will have no effect if device or queue do not exist
|
|
|
|
static IOReturn hid_DisposeReleaseQueue (pRecDevice pDevice)
|
|
{
|
|
IOReturn result = kIOReturnError; // assume failure (pessimist!)
|
|
|
|
if (HIDIsValidDevice(pDevice)) // need valid device
|
|
{
|
|
if (pDevice->queue) // and queue
|
|
{
|
|
// stop queue
|
|
result = (*(IOHIDQueueInterface**) pDevice->queue)->stop (pDevice->queue);
|
|
if (kIOReturnSuccess != result)
|
|
HIDREPORTERRORNUM ("hid_DisposeReleaseQueue - Failed to stop queue.", result);
|
|
// dispose of queue
|
|
result = (*(IOHIDQueueInterface**) pDevice->queue)->dispose (pDevice->queue);
|
|
if (kIOReturnSuccess != result)
|
|
HIDREPORTERRORNUM ("hid_DisposeReleaseQueue - Failed to dipose queue.", result);
|
|
// release the queue
|
|
result = (*(IOHIDQueueInterface**) pDevice->queue)->Release (pDevice->queue);
|
|
if (kIOReturnSuccess != result)
|
|
HIDREPORTERRORNUM ("hid_DisposeReleaseQueue - Failed to release queue.", result);
|
|
|
|
pDevice->queue = NULL;
|
|
}
|
|
else
|
|
HIDREPORTERROR ("hid_DisposeReleaseQueue - no queue.");
|
|
}
|
|
else
|
|
HIDREPORTERROR ("hid_DisposeReleaseQueue - Invalid device.");
|
|
return result;
|
|
}
|
|
|
|
|
|
// ---------------------------------
|
|
// completely removes all elements from queue and releases queue and closes device interface
|
|
// does not release device interfaces, application must call HIDReleaseDeviceList on exit
|
|
|
|
static unsigned long HIDDequeueDevice (pRecDevice pDevice)
|
|
{
|
|
IOReturn result = kIOReturnSuccess;
|
|
|
|
if (HIDIsValidDevice(pDevice))
|
|
{
|
|
if ((pDevice->interface) && (pDevice->queue))
|
|
{
|
|
// iterate through elements and if queued, remove
|
|
pRecElement pElement = HIDGetFirstDeviceElement (pDevice, kHIDElementTypeIO);
|
|
while (pElement)
|
|
{
|
|
if ((*(IOHIDQueueInterface**) pDevice->queue)->hasElement (pDevice->queue, pElement->cookie))
|
|
{
|
|
result = (*(IOHIDQueueInterface**) pDevice->queue)->removeElement (pDevice->queue, pElement->cookie);
|
|
if (kIOReturnSuccess != result)
|
|
HIDREPORTERRORNUM ("HIDDequeueDevice - Failed to remove element from queue.", result);
|
|
}
|
|
pElement = HIDGetNextDeviceElement (pElement, kHIDElementTypeIO);
|
|
}
|
|
}
|
|
// ensure queue is disposed and released
|
|
// interface will be closed and released on call to HIDReleaseDeviceList
|
|
result = hid_DisposeReleaseQueue (pDevice);
|
|
if (kIOReturnSuccess != result)
|
|
HIDREPORTERRORNUM ("removeElement - Failed to dispose and release queue.", result);
|
|
#if USE_ASYNC_EVENTS
|
|
else if (NULL != pDevice->queueRunLoopSource)
|
|
{
|
|
if (CFRunLoopContainsSource(CFRunLoopGetCurrent(), pDevice->queueRunLoopSource, kCFRunLoopDefaultMode))
|
|
CFRunLoopRemoveSource(CFRunLoopGetCurrent(), pDevice->queueRunLoopSource, kCFRunLoopDefaultMode);
|
|
CFRelease(pDevice->queueRunLoopSource);
|
|
pDevice->queueRunLoopSource = NULL;
|
|
}
|
|
#endif USE_ASYNC_EVENTS
|
|
}
|
|
else
|
|
{
|
|
HIDREPORTERROR ("HIDDequeueDevice - Invalid device.");
|
|
result = kIOReturnBadArgument;
|
|
}
|
|
return result;
|
|
}
|
|
|
|
// ---------------------------------
|
|
// releases all device queues for quit or rebuild (must be called)
|
|
// does not release device interfaces, application must call HIDReleaseDeviceList on exit
|
|
|
|
static unsigned long HIDReleaseAllDeviceQueues (void)
|
|
{
|
|
IOReturn result = kIOReturnBadArgument;
|
|
pRecDevice pDevice = HIDGetFirstDevice ();
|
|
|
|
while (pDevice)
|
|
{
|
|
result = HIDDequeueDevice (pDevice);
|
|
if (kIOReturnSuccess != result)
|
|
HIDREPORTERRORNUM ("HIDReleaseAllDeviceQueues - Could not dequeue device.", result);
|
|
pDevice = HIDGetNextDevice (pDevice);
|
|
}
|
|
return result;
|
|
}
|
|
|
|
|
|
// ---------------------------------
|
|
// Get the next event in the queue for a device
|
|
// elements or entire device should be queued prior to calling this with HIDQueueElement or HIDQueueDevice
|
|
// returns true if an event is avialable for the element and fills out *pHIDEvent structure, returns false otherwise
|
|
// Note: kIOReturnUnderrun returned from getNextEvent indicates an empty queue not an error condition
|
|
// Note: application should pass in a pointer to a IOHIDEventStruct cast to a void (for CFM compatibility)
|
|
|
|
static unsigned char HIDGetEvent (pRecDevice pDevice, void * pHIDEvent)
|
|
{
|
|
IOReturn result = kIOReturnBadArgument;
|
|
AbsoluteTime zeroTime = {0,0};
|
|
|
|
if (HIDIsValidDevice(pDevice))
|
|
{
|
|
if (pDevice->queue)
|
|
{
|
|
result = (*(IOHIDQueueInterface**) pDevice->queue)->getNextEvent (pDevice->queue, (IOHIDEventStruct *)pHIDEvent, zeroTime, 0);
|
|
if (kIOReturnUnderrun == result)
|
|
return false; // no events in queue not an error per say
|
|
else if (kIOReturnSuccess != result) // actual error versus just an empty queue
|
|
HIDREPORTERRORNUM ("HIDGetEvent - Could not get HID event via getNextEvent.", result);
|
|
else
|
|
return true;
|
|
}
|
|
else
|
|
HIDREPORTERROR ("HIDGetEvent - queue does not exist.");
|
|
}
|
|
else
|
|
HIDREPORTERROR ("HIDGetEvent - invalid device.");
|
|
|
|
return false; // did not get event
|
|
}
|
|
|
|
|
|
static unsigned long HIDCreateOpenDeviceInterface (UInt32 hidDevice, pRecDevice pDevice)
|
|
{
|
|
IOReturn result = kIOReturnSuccess;
|
|
HRESULT plugInResult = S_OK;
|
|
SInt32 score = 0;
|
|
IOCFPlugInInterface ** ppPlugInInterface = NULL;
|
|
|
|
if (NULL == pDevice->interface)
|
|
{
|
|
result = IOCreatePlugInInterfaceForService (hidDevice, kIOHIDDeviceUserClientTypeID,
|
|
kIOCFPlugInInterfaceID, &ppPlugInInterface, &score);
|
|
if (kIOReturnSuccess == result)
|
|
{
|
|
// Call a method of the intermediate plug-in to create the device interface
|
|
plugInResult = (*ppPlugInInterface)->QueryInterface (ppPlugInInterface,
|
|
CFUUIDGetUUIDBytes (kIOHIDDeviceInterfaceID), (void *) &(pDevice->interface));
|
|
if (S_OK != plugInResult)
|
|
HIDReportErrorNum ("CouldnÕt query HID class device interface from plugInInterface", plugInResult);
|
|
IODestroyPlugInInterface (ppPlugInInterface); // replace (*ppPlugInInterface)->Release (ppPlugInInterface)
|
|
}
|
|
else
|
|
HIDReportErrorNum ("Failed to create **plugInInterface via IOCreatePlugInInterfaceForService.", result);
|
|
}
|
|
if (NULL != pDevice->interface)
|
|
{
|
|
result = (*(IOHIDDeviceInterface**)pDevice->interface)->open (pDevice->interface, 0);
|
|
if (kIOReturnSuccess != result)
|
|
HIDReportErrorNum ("Failed to open pDevice->interface via open.", result);
|
|
}
|
|
return result;
|
|
}
|
|
|
|
|
|
// ---------------------------------
|
|
// adds device to linked list of devices passed in (handles NULL lists properly)
|
|
// (returns where you just stored it)
|
|
static pRecDevice* hid_AddDevice (pRecDevice *ppListDeviceHead, pRecDevice pNewDevice)
|
|
{
|
|
pRecDevice* result = NULL;
|
|
|
|
if (NULL == *ppListDeviceHead)
|
|
result = ppListDeviceHead;
|
|
else
|
|
{
|
|
pRecDevice pDevicePrevious = NULL, pDevice = *ppListDeviceHead;
|
|
while (pDevice)
|
|
{
|
|
pDevicePrevious = pDevice;
|
|
pDevice = pDevicePrevious->pNext;
|
|
}
|
|
result = &pDevicePrevious->pNext;
|
|
}
|
|
pNewDevice->pNext = NULL;
|
|
|
|
*result = pNewDevice;
|
|
|
|
return result;
|
|
}
|
|
|
|
static pRecDevice hid_BuildDevice (io_object_t hidDevice)
|
|
{
|
|
pRecDevice pDevice = (pRecDevice) malloc (sizeof (recDevice));
|
|
|
|
if (NULL != pDevice)
|
|
{
|
|
// get dictionary for HID properties
|
|
CFMutableDictionaryRef hidProperties = 0;
|
|
kern_return_t result = IORegistryEntryCreateCFProperties (hidDevice, &hidProperties, kCFAllocatorDefault, kNilOptions);
|
|
|
|
// clear record
|
|
bzero(pDevice, sizeof(recDevice));
|
|
|
|
if ((result == KERN_SUCCESS) && (NULL != hidProperties))
|
|
{
|
|
pRecElement pCurrentElement = NULL;
|
|
// create device interface
|
|
result = HIDCreateOpenDeviceInterface (hidDevice, pDevice);
|
|
if (kIOReturnSuccess != result)
|
|
HIDReportErrorNum ("HIDCreateOpenDeviceInterface failed.", result);
|
|
hid_GetDeviceInfo (hidDevice, hidProperties, pDevice); // hidDevice used to find parents in registry tree
|
|
// set current device for use in getting elements
|
|
gCurrentGetDevice = pDevice;
|
|
// Add all elements
|
|
hid_GetCollectionElements (hidProperties, &pCurrentElement);
|
|
gCurrentGetDevice = NULL;
|
|
CFRelease (hidProperties);
|
|
}
|
|
else
|
|
HIDReportErrorNum ("IORegistryEntryCreateCFProperties error when creating deviceProperties.", result);
|
|
}
|
|
else
|
|
HIDReportError ("malloc error when allocating pRecDevice.");
|
|
return pDevice;
|
|
}
|
|
|
|
|
|
|
|
#if USE_NOTIFICATIONS
|
|
//================================================================================================
|
|
//
|
|
// hid_DeviceNotification
|
|
//
|
|
// This routine will get called whenever any kIOGeneralInterest notification happens. We are
|
|
// interested in the kIOMessageServiceIsTerminated message so that's what we look for. Other
|
|
// messages are defined in IOMessage.h.
|
|
//
|
|
//================================================================================================
|
|
//
|
|
static void hid_DeviceNotification( void *refCon,
|
|
io_service_t service,
|
|
natural_t messageType,
|
|
void *messageArgument )
|
|
{
|
|
pRecDevice pDevice = (pRecDevice) refCon;
|
|
|
|
if (messageType == kIOMessageServiceIsTerminated)
|
|
{
|
|
//printf("Device 0x%08x \"%s\"removed.\n", service, pDevice->product);
|
|
// ryan added this.
|
|
if (pDevice->disconnect == DISCONNECT_CONNECTED)
|
|
pDevice->disconnect = DISCONNECT_TELLUSER;
|
|
|
|
// Free the data we're no longer using now that the device is going away
|
|
// ryan commented this out.
|
|
//hid_DisposeDevice (pDevice);
|
|
}
|
|
}
|
|
#else
|
|
|
|
static void hid_RemovalCallbackFunction(void * target, IOReturn result, void * refcon, void * sender)
|
|
{
|
|
// ryan commented this out.
|
|
//hid_DisposeDevice ((pRecDevice) target);
|
|
|
|
// ryan added this.
|
|
pRecDevice = (pRecDevice) target;
|
|
if (pDevice->disconnect == DISCONNECT_CONNECTED)
|
|
pDevice->disconnect = DISCONNECT_TELLUSER;
|
|
}
|
|
|
|
#endif USE_NOTIFICATIONS
|
|
|
|
|
|
|
|
static void hid_AddDevices (void *refCon, io_iterator_t iterator)
|
|
{
|
|
// NOTE: refcon passed in is used to point to the device list head
|
|
pRecDevice* pListDeviceHead = (pRecDevice*) refCon;
|
|
IOReturn result = kIOReturnSuccess;
|
|
io_object_t ioHIDDeviceObject = 0;
|
|
|
|
while ((ioHIDDeviceObject = IOIteratorNext (iterator)) != 0)
|
|
{
|
|
pRecDevice* pNewDeviceAt = NULL;
|
|
pRecDevice pNewDevice = hid_BuildDevice (ioHIDDeviceObject);
|
|
if (pNewDevice)
|
|
{
|
|
#if 0 // set true for verbose output
|
|
printf("\nhid_AddDevices: pNewDevice = {t: \"%s\", v: %ld, p: %ld, v: %ld, m: \"%s\", " \
|
|
"p: \"%s\", l: %ld, u: %4.4lX:%4.4lX, #e: %ld, #f: %ld, #i: %ld, #o: %ld, " \
|
|
"#c: %ld, #a: %ld, #b: %ld, #h: %ld, #s: %ld, #d: %ld, #w: %ld}.",
|
|
pNewDevice->transport,
|
|
pNewDevice->vendorID,
|
|
pNewDevice->productID,
|
|
pNewDevice->version,
|
|
pNewDevice->manufacturer,
|
|
pNewDevice->product,
|
|
pNewDevice->locID,
|
|
pNewDevice->usagePage,
|
|
pNewDevice->usage,
|
|
pNewDevice->totalElements,
|
|
pNewDevice->features,
|
|
pNewDevice->inputs,
|
|
pNewDevice->outputs,
|
|
pNewDevice->collections,
|
|
pNewDevice->axis,
|
|
pNewDevice->buttons,
|
|
pNewDevice->hats,
|
|
pNewDevice->sliders,
|
|
pNewDevice->dials,
|
|
pNewDevice->wheels
|
|
);
|
|
fflush(stdout);
|
|
#elif 0 // otherwise output brief description
|
|
printf("\nhid_AddDevices: pNewDevice = {m: \"%s\" p: \"%s\", vid: %ld, pid: %ld, loc: %8.8lX, usage: %4.4lX:%4.4lX}.",
|
|
pNewDevice->manufacturer,
|
|
pNewDevice->product,
|
|
pNewDevice->vendorID,
|
|
pNewDevice->productID,
|
|
pNewDevice->locID,
|
|
pNewDevice->usagePage,
|
|
pNewDevice->usage
|
|
);
|
|
fflush(stdout);
|
|
#endif
|
|
pNewDeviceAt = hid_AddDevice (pListDeviceHead, pNewDevice);
|
|
}
|
|
|
|
#if USE_NOTIFICATIONS
|
|
// Register for an interest notification of this device being removed. Use a reference to our
|
|
// private data as the refCon which will be passed to the notification callback.
|
|
result = IOServiceAddInterestNotification( gNotifyPort, // notifyPort
|
|
ioHIDDeviceObject, // service
|
|
kIOGeneralInterest, // interestType
|
|
hid_DeviceNotification, // callback
|
|
pNewDevice, // refCon
|
|
(io_object_t*) &pNewDevice->notification); // notification
|
|
if (KERN_SUCCESS != result)
|
|
HIDReportErrorNum ("hid_AddDevices: IOServiceAddInterestNotification error: x0%8.8lX.", result);
|
|
#else
|
|
result = (*(IOHIDDeviceInterface**)pNewDevice->interface)->setRemovalCallback (pNewDevice->interface, hid_RemovalCallbackFunction,pNewDeviceAt,0);
|
|
#endif USE_NOTIFICATIONS
|
|
|
|
// release the device object, it is no longer needed
|
|
result = IOObjectRelease (ioHIDDeviceObject);
|
|
if (KERN_SUCCESS != result)
|
|
HIDReportErrorNum ("hid_AddDevices: IOObjectRelease error with ioHIDDeviceObject.", result);
|
|
}
|
|
}
|
|
|
|
|
|
static Boolean HIDBuildDeviceList (UInt32 usagePage, UInt32 usage)
|
|
{
|
|
IOReturn result = kIOReturnSuccess;
|
|
mach_port_t masterPort = 0;
|
|
|
|
if (NULL != gpDeviceList)
|
|
HIDReleaseDeviceList ();
|
|
|
|
result = IOMasterPort (bootstrap_port, &masterPort);
|
|
if (kIOReturnSuccess != result)
|
|
HIDReportErrorNum ("IOMasterPort error with bootstrap_port.", result);
|
|
else
|
|
{
|
|
CFMutableDictionaryRef hidMatchDictionary = NULL;
|
|
|
|
// Set up matching dictionary to search the I/O Registry for HID devices we are interested in. Dictionary reference is NULL if error.
|
|
{
|
|
CFNumberRef refUsage = NULL, refUsagePage = NULL;
|
|
|
|
// Set up a matching dictionary to search I/O Registry by class name for all HID class devices.
|
|
hidMatchDictionary = IOServiceMatching (kIOHIDDeviceKey);
|
|
if (NULL != hidMatchDictionary)
|
|
{
|
|
if (usagePage)
|
|
{
|
|
// Add key for device type (joystick, in this case) to refine the matching dictionary.
|
|
refUsagePage = CFNumberCreate (kCFAllocatorDefault, kCFNumberLongType, &usagePage);
|
|
CFDictionarySetValue (hidMatchDictionary, CFSTR (kIOHIDPrimaryUsagePageKey), refUsagePage);
|
|
CFRelease (refUsagePage);
|
|
if (usage)
|
|
{
|
|
refUsage = CFNumberCreate (kCFAllocatorDefault, kCFNumberLongType, &usage);
|
|
CFDictionarySetValue (hidMatchDictionary, CFSTR (kIOHIDPrimaryUsageKey), refUsage);
|
|
CFRelease (refUsage);
|
|
}
|
|
}
|
|
CFRetain(hidMatchDictionary);
|
|
}
|
|
else
|
|
HIDReportError ("Failed to get HID CFMutableDictionaryRef via IOServiceMatching.");
|
|
}
|
|
|
|
#if USE_NOTIFICATIONS
|
|
// Create a notification port and add its run loop event source to our run loop
|
|
// This is how async notifications get set up.
|
|
{
|
|
CFRunLoopSourceRef runLoopSource;
|
|
|
|
gNotifyPort = IONotificationPortCreate(masterPort);
|
|
runLoopSource = IONotificationPortGetRunLoopSource(gNotifyPort);
|
|
|
|
gRunLoop = CFRunLoopGetCurrent();
|
|
CFRunLoopAddSource(gRunLoop, runLoopSource, kCFRunLoopDefaultMode);
|
|
|
|
// Now set up a notification to be called when a device is first matched by I/O Kit.
|
|
result = IOServiceAddMatchingNotification(gNotifyPort, // notifyPort
|
|
kIOFirstMatchNotification, // notificationType
|
|
hidMatchDictionary, // matching
|
|
hid_AddDevices, // callback
|
|
&gpDeviceList, // refCon
|
|
&gAddedIter // notification
|
|
);
|
|
|
|
// call it now to add all existing devices
|
|
hid_AddDevices(&gpDeviceList,gAddedIter);
|
|
return true;
|
|
}
|
|
#else
|
|
{
|
|
io_iterator_t hidObjectIterator = NULL;
|
|
|
|
// Now search I/O Registry for matching devices.
|
|
result = IOServiceGetMatchingServices (masterPort, hidMatchDictionary, &hidObjectIterator);
|
|
if (kIOReturnSuccess != result)
|
|
HIDReportErrorNum ("Failed to create IO object iterator, error:", result);
|
|
else if (NULL == hidObjectIterator) // likely no HID devices which matched selection criteria are connected
|
|
HIDReportError ("Warning: Could not find any matching devices, thus iterator creation failed.");
|
|
|
|
if (NULL != hidObjectIterator)
|
|
{
|
|
hid_AddDevices(&gpDeviceList,hidObjectIterator);
|
|
|
|
result = IOObjectRelease (hidObjectIterator); // release the iterator
|
|
if (kIOReturnSuccess != result)
|
|
HIDReportErrorNum ("IOObjectRelease error with hidObjectIterator.", result);
|
|
|
|
gNumDevices = hid_CountCurrentDevices ();
|
|
return true;
|
|
}
|
|
}
|
|
#endif USE_NOTIFICATIONS
|
|
// IOServiceGetMatchingServices consumes a reference to the dictionary, so we don't need to release the dictionary ref.
|
|
hidMatchDictionary = NULL;
|
|
}
|
|
return false;
|
|
}
|
|
|
|
// ---------------------------------
|
|
// release list built by above function
|
|
// MUST be called prior to application exit to properly release devices
|
|
// if not called (or app crashes) devices can be recovered by pluging into different location in USB chain
|
|
|
|
static void HIDReleaseDeviceList (void)
|
|
{
|
|
while (NULL != gpDeviceList)
|
|
gpDeviceList = hid_DisposeDevice (gpDeviceList); // dispose current device return next device will set gpDeviceList to NULL
|
|
gNumDevices = 0;
|
|
}
|
|
|
|
// ---------------------------------
|
|
// get the first device in the device list
|
|
// returns NULL if no list exists
|
|
|
|
static pRecDevice HIDGetFirstDevice (void)
|
|
{
|
|
return gpDeviceList;
|
|
}
|
|
|
|
// ---------------------------------
|
|
// get next device in list given current device as parameter
|
|
// returns NULL if end of list
|
|
|
|
static pRecDevice HIDGetNextDevice (pRecDevice pDevice)
|
|
{
|
|
if (NULL != pDevice)
|
|
return pDevice->pNext;
|
|
else
|
|
return NULL;
|
|
}
|
|
|
|
// ---------------------------------
|
|
// get the first element of device passed in as parameter
|
|
// returns NULL if no list exists or device does not exists or is NULL
|
|
static pRecElement HIDGetFirstDeviceElement (pRecDevice pDevice, HIDElementTypeMask typeMask)
|
|
{
|
|
if (HIDIsValidDevice(pDevice))
|
|
{
|
|
if (hid_MatchElementTypeMask (pDevice->pListElements->type, typeMask)) // ensure first type matches
|
|
return pDevice->pListElements;
|
|
else
|
|
return HIDGetNextDeviceElement (pDevice->pListElements, typeMask);
|
|
}
|
|
else
|
|
return NULL;
|
|
}
|
|
|
|
// ---------------------------------
|
|
// get next element of given device in list given current element as parameter
|
|
// will walk down each collection then to next element or collection (depthwise traverse)
|
|
// returns NULL if end of list
|
|
// uses mask of HIDElementTypeMask to restrict element found
|
|
// use kHIDElementTypeIO to get previous HIDGetNextDeviceElement functionality
|
|
static pRecElement HIDGetNextDeviceElement (pRecElement pElement, HIDElementTypeMask typeMask)
|
|
{
|
|
// should only have elements passed in (though someone could mix calls and pass us a collection)
|
|
// collection means return the next child or sibling (in that order)
|
|
// element means returnt he next sibling (as elements can't have children
|
|
if (NULL != pElement)
|
|
{
|
|
if (pElement->pChild)
|
|
{
|
|
if (pElement->type != kIOHIDElementTypeCollection)
|
|
HIDReportError ("Malformed element list: found child of element.");
|
|
else
|
|
return hid_GetDeviceElement (pElement->pChild, typeMask); // return the child of this element
|
|
}
|
|
else if (pElement->pSibling)
|
|
{
|
|
return hid_GetDeviceElement (pElement->pSibling, typeMask); //return the sibling of this element
|
|
}
|
|
else // at end back up correctly
|
|
{
|
|
pRecElement pPreviousElement = NULL;
|
|
// malformed device ending in collection
|
|
if (pElement->type == kIOHIDElementTypeCollection)
|
|
HIDReportError ("Malformed device: found collection at end of element chain.");
|
|
// walk back up tree to element prior to first collection ecountered and take next element
|
|
while (NULL != pElement->pPrevious)
|
|
{
|
|
pPreviousElement = pElement;
|
|
pElement = pElement->pPrevious; // look at previous element
|
|
// if we have a collection and the previous element is the branch element (should have both a colection and next element attached to it)
|
|
// if we found a collection, which we are not at the sibling level that actually does have siblings
|
|
if (((pElement->type == kIOHIDElementTypeCollection) && (pPreviousElement != pElement->pSibling) && pElement->pSibling) ||
|
|
// or if we are at the top
|
|
(NULL == pElement->pPrevious)) // at top of tree
|
|
break;
|
|
}
|
|
if (NULL == pElement->pPrevious)
|
|
return NULL; // got to top of list with only a collection as the first element
|
|
// now we must have been down the child route so go down the sibling route
|
|
pElement = pElement->pSibling; // element of interest
|
|
return hid_GetDeviceElement (pElement, typeMask); // otherwise return this element
|
|
}
|
|
}
|
|
return NULL;
|
|
}
|
|
|
|
|
|
// return true if this is a valid device pointer
|
|
Boolean HIDIsValidDevice(const pRecDevice pSearchDevice)
|
|
{
|
|
pRecDevice pDevice = gpDeviceList;
|
|
|
|
while (pDevice)
|
|
{
|
|
if (pDevice == pSearchDevice)
|
|
return true;
|
|
pDevice = pDevice->pNext;
|
|
}
|
|
return false;
|
|
}
|
|
|
|
|
|
static IOReturn hid_CreateQueue (pRecDevice pDevice)
|
|
{
|
|
IOReturn result = kIOReturnError; // assume failure (pessimist!)
|
|
|
|
if (HIDIsValidDevice(pDevice))
|
|
{
|
|
if (NULL == pDevice->queue) // do we already have a queue
|
|
{
|
|
if (NULL != pDevice->interface)
|
|
{
|
|
pDevice->queue = (void *) (*(IOHIDDeviceInterface**) pDevice->interface)->allocQueue (pDevice->interface); // alloc queue
|
|
if (pDevice->queue)
|
|
{
|
|
result = (*(IOHIDQueueInterface**) pDevice->queue)->create (pDevice->queue, 0, kDeviceQueueSize); // create actual queue
|
|
if (kIOReturnSuccess != result)
|
|
HIDREPORTERRORNUM ("hid_CreateQueue - Failed to create queue via create", result);
|
|
}
|
|
else
|
|
{
|
|
HIDREPORTERROR ("hid_CreateQueue - Failed to alloc IOHIDQueueInterface ** via allocQueue");
|
|
result = kIOReturnError; // synthesis error
|
|
}
|
|
}
|
|
else
|
|
HIDREPORTERRORNUM ("hid_CreateQueue - Device inteface does not exist for queue creation", result);
|
|
}
|
|
}
|
|
else
|
|
HIDREPORTERRORNUM ("hid_CreateQueue - Invalid Device", result);
|
|
return result;
|
|
}
|
|
|
|
static unsigned long HIDQueueDevice (pRecDevice pDevice)
|
|
{
|
|
IOReturn result = kIOReturnError; // assume failure (pessimist!)
|
|
pRecElement pElement;
|
|
|
|
if (HIDIsValidDevice(pDevice))
|
|
{
|
|
// error checking
|
|
if (NULL == pDevice)
|
|
{
|
|
HIDREPORTERROR ("HIDQueueDevice - Device does not exist.");
|
|
return kIOReturnBadArgument;
|
|
}
|
|
if (NULL == pDevice->interface) // must have interface
|
|
{
|
|
HIDREPORTERROR ("HIDQueueDevice - Device does not have interface.");
|
|
return kIOReturnError;
|
|
}
|
|
if (NULL == pDevice->queue) // if no queue create queue
|
|
result = hid_CreateQueue (pDevice);
|
|
if ((kIOReturnSuccess != result) || (NULL == pDevice->queue))
|
|
{
|
|
HIDREPORTERRORNUM ("HIDQueueDevice - problem creating queue.", result);
|
|
if (kIOReturnSuccess != result)
|
|
return result;
|
|
else
|
|
return kIOReturnError;
|
|
}
|
|
|
|
// stop queue
|
|
result = (*(IOHIDQueueInterface**) pDevice->queue)->stop (pDevice->queue);
|
|
if (kIOReturnSuccess != result)
|
|
HIDREPORTERRORNUM ("HIDQueueDevice - Failed to stop queue.", result);
|
|
|
|
// queue element
|
|
//¥ pElement = HIDGetFirstDeviceElement (pDevice, kHIDElementTypeIO);
|
|
pElement = HIDGetFirstDeviceElement (pDevice, kHIDElementTypeInput | kHIDElementTypeFeature);
|
|
|
|
while (pElement)
|
|
{
|
|
if (!(*(IOHIDQueueInterface**) pDevice->queue)->hasElement (pDevice->queue, pElement->cookie))
|
|
{
|
|
result = (*(IOHIDQueueInterface**) pDevice->queue)->addElement (pDevice->queue, pElement->cookie, 0);
|
|
if (kIOReturnSuccess != result)
|
|
HIDREPORTERRORNUM ("HIDQueueDevice - Failed to add element to queue.", result);
|
|
}
|
|
//¥ pElement = HIDGetNextDeviceElement (pElement, kHIDElementTypeIO);
|
|
pElement = HIDGetNextDeviceElement (pElement, kHIDElementTypeInput | kHIDElementTypeFeature);
|
|
}
|
|
|
|
// start queue
|
|
result = (*(IOHIDQueueInterface**) pDevice->queue)->start (pDevice->queue);
|
|
if (kIOReturnSuccess != result)
|
|
HIDREPORTERRORNUM ("HIDQueueDevice - Failed to start queue.", result);
|
|
|
|
}
|
|
else
|
|
HIDREPORTERROR ("HIDQueueDevice - Invalid device.");
|
|
|
|
return result;
|
|
}
|
|
|
|
|
|
/* -- END HID UTILITIES -- */
|
|
|
|
|
|
static int available_mice = 0;
|
|
static pRecDevice *devices = NULL;
|
|
|
|
|
|
/* returns non-zero if (a <= b). */
|
|
typedef unsigned long long ui64;
|
|
static inline int oldEvent(const AbsoluteTime *a, const AbsoluteTime *b)
|
|
{
|
|
#if 0 // !!! FIXME: doesn't work, timestamps aren't reliable.
|
|
const ui64 a64 = (((unsigned long long) a->hi) << 32) | a->lo;
|
|
const ui64 b64 = (((unsigned long long) b->hi) << 32) | b->lo;
|
|
#endif
|
|
return 0;
|
|
} /* oldEvent */
|
|
|
|
static int poll_mouse(pRecDevice mouse, ManyMouseEvent *outevent)
|
|
{
|
|
int unhandled = 1;
|
|
while (unhandled) /* read until failure or valid event. */
|
|
{
|
|
pRecElement recelem;
|
|
IOHIDEventStruct event;
|
|
|
|
if (!HIDGetEvent(mouse, &event))
|
|
return(0); /* no new event. */
|
|
|
|
unhandled = 0; /* will reset if necessary. */
|
|
recelem = HIDGetFirstDeviceElement(mouse, kHIDElementTypeInput);
|
|
while (recelem != NULL)
|
|
{
|
|
if (recelem->cookie == event.elementCookie)
|
|
break;
|
|
recelem = HIDGetNextDeviceElement(recelem, kHIDElementTypeInput);
|
|
} /* while */
|
|
|
|
if (recelem == NULL)
|
|
continue; /* unknown device element. Can this actually happen? */
|
|
|
|
outevent->value = event.value;
|
|
if (recelem->usagePage == kHIDPage_GenericDesktop)
|
|
{
|
|
/*
|
|
* some devices (two-finger-scroll trackpads?) seem to give
|
|
* a flood of events with values of zero for every legitimate
|
|
* event. Throw these zero events out.
|
|
*/
|
|
if (outevent->value == 0)
|
|
unhandled = 1;
|
|
else
|
|
{
|
|
switch (recelem->usage)
|
|
{
|
|
case kHIDUsage_GD_X:
|
|
case kHIDUsage_GD_Y:
|
|
if (oldEvent(&event.timestamp, &mouse->lastScrollTime))
|
|
unhandled = 1;
|
|
else
|
|
{
|
|
outevent->type = MANYMOUSE_EVENT_RELMOTION;
|
|
if (recelem->usage == kHIDUsage_GD_X)
|
|
outevent->item = 0;
|
|
else
|
|
outevent->item = 1;
|
|
} /* else */
|
|
break;
|
|
|
|
case kHIDUsage_GD_Wheel:
|
|
memcpy(&mouse->lastScrollTime, &event.timestamp,
|
|
sizeof (AbsoluteTime));
|
|
outevent->type = MANYMOUSE_EVENT_SCROLL;
|
|
outevent->item = 0; /* !!! FIXME: horiz scroll? */
|
|
break;
|
|
|
|
default: /* !!! FIXME: absolute motion? */
|
|
unhandled = 1;
|
|
} /* switch */
|
|
} /* else */
|
|
} /* if */
|
|
|
|
else if (recelem->usagePage == kHIDPage_Button)
|
|
{
|
|
outevent->type = MANYMOUSE_EVENT_BUTTON;
|
|
outevent->item = ((int) recelem->usage) - 1;
|
|
} /* else if */
|
|
|
|
else
|
|
{
|
|
unhandled = 1;
|
|
} /* else */
|
|
} /* while */
|
|
|
|
return(1); /* got a valid event */
|
|
} /* poll_mouse */
|
|
|
|
|
|
static void macosx_hidmanager_quit(void)
|
|
{
|
|
HIDReleaseAllDeviceQueues();
|
|
HIDReleaseDeviceList();
|
|
free(devices);
|
|
devices = NULL;
|
|
available_mice = 0;
|
|
} /* macosx_hidmanager_quit */
|
|
|
|
|
|
static int macosx_hidmanager_init(void)
|
|
{
|
|
macosx_hidmanager_quit(); /* just in case... */
|
|
|
|
if (!HIDBuildDeviceList(kHIDPage_GenericDesktop, kHIDUsage_GD_Mouse))
|
|
return(-1);
|
|
|
|
available_mice = HIDCountDevices();
|
|
if (available_mice > 0)
|
|
{
|
|
int i;
|
|
pRecDevice dev = NULL;
|
|
|
|
dev = HIDGetFirstDevice();
|
|
devices = (pRecDevice *) malloc(sizeof (pRecDevice) * available_mice);
|
|
if ((devices == NULL) || (dev == NULL))
|
|
{
|
|
macosx_hidmanager_quit();
|
|
return(-1);
|
|
} /* if */
|
|
|
|
for (i = 0; i < available_mice; i++)
|
|
{
|
|
if (dev == NULL) /* what? list ended? Truncate final list... */
|
|
available_mice = i;
|
|
|
|
if (HIDQueueDevice(dev) == kIOReturnSuccess)
|
|
devices[i] = dev;
|
|
else /* failed? Chop this device from the list... */
|
|
{
|
|
i--;
|
|
available_mice--;
|
|
} /* else */
|
|
|
|
dev = HIDGetNextDevice(dev);
|
|
} /* for */
|
|
} /* if */
|
|
|
|
return(available_mice);
|
|
} /* macosx_hidmanager_init */
|
|
|
|
|
|
static const char *macosx_hidmanager_name(unsigned int index)
|
|
{
|
|
if (index >= available_mice)
|
|
return(NULL);
|
|
|
|
return((const char *) devices[index]->product);
|
|
} /* macosx_hidmanager_name */
|
|
|
|
|
|
static int macosx_hidmanager_poll(ManyMouseEvent *event)
|
|
{
|
|
/*
|
|
* (i) is static so we iterate through all mice round-robin. This
|
|
* prevents a chatty mouse from dominating the queue.
|
|
*/
|
|
static unsigned int i = 0;
|
|
|
|
if (i >= available_mice)
|
|
i = 0; /* handle reset condition. */
|
|
|
|
if (event != NULL)
|
|
{
|
|
while (i < available_mice)
|
|
{
|
|
pRecDevice dev = devices[i];
|
|
if ((dev) && (dev->disconnect != DISCONNECT_COMPLETE))
|
|
{
|
|
event->device = i;
|
|
|
|
/* see if mouse was unplugged since last polling... */
|
|
if (dev->disconnect == DISCONNECT_TELLUSER)
|
|
{
|
|
dev->disconnect = DISCONNECT_COMPLETE;
|
|
event->type = MANYMOUSE_EVENT_DISCONNECT;
|
|
return(1);
|
|
} /* if */
|
|
|
|
if (poll_mouse(dev, event))
|
|
return(1);
|
|
} /* if */
|
|
i++;
|
|
} /* while */
|
|
} /* if */
|
|
|
|
return(0); /* no new events */
|
|
} /* macosx_hidmanager_poll */
|
|
|
|
#else
|
|
|
|
static int macosx_hidmanager_init(void) { return(-1); }
|
|
static void macosx_hidmanager_quit(void) {}
|
|
static const char *macosx_hidmanager_name(unsigned int index) { return(0); }
|
|
static int macosx_hidmanager_poll(ManyMouseEvent *event) { return(0); }
|
|
|
|
#endif /* MacOSX blocker */
|
|
|
|
ManyMouseDriver ManyMouseDriver_hidmanager =
|
|
{
|
|
macosx_hidmanager_init,
|
|
macosx_hidmanager_quit,
|
|
macosx_hidmanager_name,
|
|
macosx_hidmanager_poll
|
|
};
|
|
|
|
/* end of macosx_hidmanager.c ... */
|
|
|