/*
* Copyright (c) 2002-2004 LWJGL Project
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are
* met:
*
* * Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
*
* * Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
*
* * Neither the name of 'LWJGL' nor the names of
* its contributors may be used to endorse or promote products derived
* from this software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
* TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
* PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
* CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
* EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
* PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
* PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
* LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
* NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
/**
* $Id$
*
* Mac OS X mouse handling.
*
* @author elias_naur <elias_naur@users.sourceforge.net>
* @version $Revision$
*/
#include "hid.h"
#include "tools.h"
#include "common_tools.h"
static void searchDictionary(CFDictionaryRef dict, hid_device_t *hid_dev, int num_cookies, hid_cookie_t *hid_cookies);
static void searchObject(CFTypeRef object, hid_device_t *hid_dev, int num_cookies, hid_cookie_t *hid_cookies);
static void printCFString(CFStringRef str) {
CFIndex buffer_size = CFStringGetLength(str) + 1;
char * buffer = (char *)malloc(buffer_size);
if (buffer != NULL) {
if (CFStringGetCString(str, buffer, buffer_size, CFStringGetSystemEncoding()))
printf("%s", buffer);
free(buffer);
}
}
static void printCFNumber(CFNumberRef num) {
long number;
if (CFNumberGetValue(num, kCFNumberLongType, &number))
printf("0x%lx (%ld)", number, number);
}
static void printProperty(CFDictionaryRef dict, CFStringRef key) {
CFTypeRef val = CFDictionaryGetValue(dict, key);
if (val != NULL) {
CFTypeID type = CFGetTypeID(val);
/* if (type == CFArrayGetTypeID()) printf("array\n");
else if (type == CFBooleanGetTypeID()) printf("boolean\n");
else if (type == CFDictionaryGetTypeID()) printf("dictionary\n");
else*/ if (type == CFNumberGetTypeID()) printCFNumber((CFNumberRef)val);
else if (type == CFStringGetTypeID()) printCFString((CFStringRef)val);
else printf("<unknown object type>\n");
}
}
static void closeDeviceAndQueue(hid_device_t *hid_dev) {
(*hid_dev->device_queue)->dispose(hid_dev->device_queue);
(*hid_dev->device_queue)->Release(hid_dev->device_queue);
(*hid_dev->device_interface)->close(hid_dev->device_interface);
}
static void closeDeviceAll(hid_device_t *hid_dev) {
closeDeviceAndQueue(hid_dev);
CFRelease(hid_dev->cookie_map);
}
static IOHIDQueueInterface **allocDeviceQueue(IOHIDDeviceInterface **device_interface, int buffer_size) {
IOHIDQueueInterface **device_queue = (*device_interface)->allocQueue(device_interface);
if (device_queue == NULL)
return false;
HRESULT err = (*device_queue)->create(device_queue, 0, buffer_size);
if (err != S_OK) {
(*device_queue)->Release(device_queue);
return NULL;
}
return device_queue;
}
static void searchArray(CFArrayRef array, hid_device_t *hid_dev, int num_cookies, hid_cookie_t *hid_cookies) {
int size = CFArrayGetCount(array);
for (int i = 0; i < size; i++) {
CFTypeRef value = (CFTypeRef)CFArrayGetValueAtIndex(array, i);
searchObject(value, hid_dev, num_cookies, hid_cookies);
}
}
static void searchObject(CFTypeRef object, hid_device_t *hid_dev, int num_cookies, hid_cookie_t *hid_cookies) {
CFTypeID type = CFGetTypeID(object);
if (type == CFArrayGetTypeID()) searchArray((CFArrayRef)object, hid_dev, num_cookies, hid_cookies);
else if (type == CFDictionaryGetTypeID()) searchDictionary((CFDictionaryRef)object, hid_dev, num_cookies, hid_cookies);
else printf("<unknown object>\n");
}
static void searchDictionaryElement(CFDictionaryRef dict, CFStringRef key, hid_device_t *hid_dev, int num_cookies, hid_cookie_t *hid_cookies) {
CFTypeRef object = CFDictionaryGetValue(dict, key);
if (object != NULL)
searchObject(object, hid_dev, num_cookies, hid_cookies);
}
static void addToDeviceQueue(hid_device_t *hid_dev, IOHIDElementCookie cookie, int index) {
HRESULT result = (*hid_dev->device_queue)->addElement(hid_dev->device_queue, cookie, 0);
if (result != S_OK) {
printfDebug("Could not add cookie to queue\n");
return;
}
CFDictionaryAddValue(hid_dev->cookie_map, cookie, (void *)index);
}
static void searchDictionary(CFDictionaryRef dict, hid_device_t *hid_dev, int num_cookies, hid_cookie_t *hid_cookies) {
searchDictionaryElement(dict, CFSTR(kIOHIDElementKey), hid_dev, num_cookies, hid_cookies);
long cookie_num;
long usage;
long usage_page;
if (!getDictLong(dict, CFSTR(kIOHIDElementCookieKey), &cookie_num) ||
!getDictLong(dict, CFSTR(kIOHIDElementUsageKey), &usage) ||
!getDictLong(dict, CFSTR(kIOHIDElementUsagePageKey), &usage_page))
return;
for (int i = 0; i < num_cookies; i++) {
if (hid_cookies[i].usage_page != kHIDPage_Undefined && hid_cookies[i].usage != kHIDUsage_Undefined &&
usage_page == hid_cookies[i].usage_page && usage == hid_cookies[i].usage) {
addToDeviceQueue(hid_dev, (IOHIDElementCookie)cookie_num, i);
}
}
}
static bool initDevice(hid_device_t *hid_dev, io_object_t hid_device, CFDictionaryRef dict, int num_cookies, hid_cookie_t *hid_cookies, int buffer_size) {
io_name_t class_name;
IOCFPlugInInterface **plugin_interface;
SInt32 score;
IOReturn io_err = IOObjectGetClass(hid_device, class_name);
if (io_err != kIOReturnSuccess)
return false;
io_err = IOCreatePlugInInterfaceForService(hid_device, kIOHIDDeviceUserClientTypeID, kIOCFPlugInInterfaceID, &plugin_interface, &score);
if (io_err != kIOReturnSuccess)
return false;
HRESULT plugin_err = (*plugin_interface)->QueryInterface(plugin_interface, CFUUIDGetUUIDBytes(kIOHIDDeviceInterfaceID), (LPVOID *)(&(hid_dev->device_interface)));
(*plugin_interface)->Release(plugin_interface);
if (plugin_err != S_OK)
return false;
io_err = (*hid_dev->device_interface)->open(hid_dev->device_interface, 0);
if (io_err != kIOReturnSuccess)
return false;
hid_dev->device_queue = allocDeviceQueue(hid_dev->device_interface, buffer_size);
if (hid_dev->device_queue == NULL) {
(*hid_dev->device_interface)->close(hid_dev->device_interface);
return false;
}
hid_dev->cookie_map = CFDictionaryCreateMutable(NULL, 0, NULL, NULL);
if (hid_dev->cookie_map == NULL) {
closeDeviceAndQueue(hid_dev);
return false;
}
searchDictionary(dict, hid_dev, num_cookies, hid_cookies);
HRESULT err = (*hid_dev->device_queue)->start(hid_dev->device_queue);
if (err != S_OK) {
closeDeviceAll(hid_dev);
return false;
}
return true;
}
bool findDevice(hid_device_t *hid_dev, long device_usage_page, long device_usage, int num_cookies, hid_cookie_t *hid_cookies, int buffer_size) {
io_iterator_t device_iterator;
io_object_t hid_device;
kern_return_t kern_err;
bool success = false;
CFMutableDictionaryRef dev_props;
CFDictionaryRef matching_dic = IOServiceMatching(kIOHIDDeviceKey);
IOReturn err = IOServiceGetMatchingServices(kIOMasterPortDefault, matching_dic, &device_iterator);
if (err != kIOReturnSuccess) {
printfDebug("Could not find matching devices\n");
return false;
}
while (!success && (hid_device = IOIteratorNext(device_iterator)) != NULL) {
kern_err = IORegistryEntryCreateCFProperties(hid_device, &dev_props, kCFAllocatorDefault, kNilOptions);
if (kern_err == KERN_SUCCESS && dev_props != NULL) {
long usage;
long usage_page;
if (getDictLong(dev_props, CFSTR(kIOHIDPrimaryUsageKey), &usage) &&
getDictLong(dev_props, CFSTR(kIOHIDPrimaryUsagePageKey), &usage_page)) {
if (isDebugEnabled()) {
printf("Considering device '");
printProperty(dev_props, CFSTR(kIOHIDProductKey));
printf("', usage page %ld usage %ld\n", usage_page, usage);
}
if (usage_page == device_usage_page && usage == device_usage) {
success = initDevice(hid_dev, hid_device, dev_props, num_cookies, hid_cookies, buffer_size);
}
}
CFRelease(dev_props);
}
IOObjectRelease(hid_device);
if (success)
break;
}
IOObjectRelease(device_iterator);
return success;
}
void shutdownDevice(hid_device_t *hid_dev) {
(*hid_dev->device_queue)->stop(hid_dev->device_queue);
closeDeviceAll(hid_dev);
}
bool nextDeviceEvent(hid_device_t *hid_dev, hid_event_t *hid_event) {
IOHIDEventStruct event;
AbsoluteTime zero_time = {0, 0};
HRESULT err = (*hid_dev->device_queue)->getNextEvent(hid_dev->device_queue, &event, zero_time, 0);
if (err != S_OK)
return false;
const void *mapped_index = CFDictionaryGetValue(hid_dev->cookie_map, event.elementCookie);
hid_event->cookie_index = mapped_index;
hid_event->value = (int)event.value;
return true;
}