src/joystick/darwin/SDL_sysjoystick.c
author Sam Lantinga <slouken@libsdl.org>
Tue, 11 Sep 2001 19:00:18 +0000
changeset 172 37e3ca9254c7
child 196 c4c4b221a5e5
permissions -rw-r--r--
Date: Sat, 8 Sep 2001 04:42:23 +0200
From: Max Horn <max@quendi.de>
Subject: SDL/OSX: Joystick; Better key handling

I just finished implementing improved keyhandling for OS X (in fact
the code should be easily ported to the "normal" MacOS part of SDL, I
just had no chance yet). Works like this:
First init the mapping table statically like before. Them, it queries
the OS for the "official" key table, then iterates over all 127
scancode and gets the associates ascii code. It ignores everythng
below 32 (has to, as it would lead to many problems if we did not...
e.g. both ESC and NUM LOCk produce an ascii code 27 on my keyboard),
and all stuff above 127 is mapped to SDLK_WORLD_* simply in the order
it is encountered.
In addition, caps lock is now working, too.
The code work flawless for me, but since I only have one keyboard, I
may have not encountered some serious problem... but I am pretty
confident that it is better than the old code in most cases.


The joystick driver works fine for me, too. I think it can be added
to CVS already. It would simply be helpful if more people would test
it. Hm, I wonder if Maelstrom or GLTron has Joystick support? That
would be a wonderful test application :)


I also took the liberty of modifying some text files like BUGS,
README.CVS, README.MacOSX (which now contains the OS X docs I long
promised)
slouken@172
     1
/*
slouken@172
     2
	SDL - Simple DirectMedia Layer
slouken@172
     3
	Copyright (C) 1997, 1998, 1999, 2000, 2001  Sam Lantinga
slouken@172
     4
slouken@172
     5
	This library is free software; you can redistribute it and/or
slouken@172
     6
	modify it under the terms of the GNU Library General Public
slouken@172
     7
	License as published by the Free Software Foundation; either
slouken@172
     8
	version 2 of the License, or (at your option) any later version.
slouken@172
     9
slouken@172
    10
	This library is distributed in the hope that it will be useful,
slouken@172
    11
	but WITHOUT ANY WARRANTY; without even the implied warranty of
slouken@172
    12
	MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
slouken@172
    13
	Library General Public License for more details.
slouken@172
    14
slouken@172
    15
	You should have received a copy of the GNU Library General Public
slouken@172
    16
	License along with this library; if not, write to the Free
slouken@172
    17
	Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
slouken@172
    18
slouken@172
    19
	Sam Lantinga
slouken@172
    20
	slouken@devolution.com
slouken@172
    21
*/
slouken@172
    22
slouken@172
    23
/* SDL joystick driver for Darwn / MacOS X, based on the IOKit HID API */
slouken@172
    24
/* Written 2001 by Max Horn */
slouken@172
    25
slouken@172
    26
#include <stdio.h>
slouken@172
    27
#include <stdlib.h>
slouken@172
    28
#include <unistd.h>
slouken@172
    29
#include <ctype.h>
slouken@172
    30
#include <sys/errno.h>
slouken@172
    31
#include <sysexits.h>
slouken@172
    32
#include <mach/mach.h>
slouken@172
    33
#include <mach/mach_error.h>
slouken@172
    34
#include <IOKit/IOKitLib.h>
slouken@172
    35
#include <IOKit/IOCFPlugIn.h>
slouken@172
    36
#include <IOKit/IOUSBHIDParser.h>
slouken@172
    37
#include <IOKit/hid/IOHIDLib.h>
slouken@172
    38
#include <IOKit/hid/IOHIDKeys.h>
slouken@172
    39
#include <CoreFoundation/CoreFoundation.h>
slouken@172
    40
slouken@172
    41
#include "SDL_error.h"
slouken@172
    42
#include "SDL_joystick.h"
slouken@172
    43
#include "SDL_sysjoystick.h"
slouken@172
    44
#include "SDL_joystick_c.h"
slouken@172
    45
slouken@172
    46
struct recElement
slouken@172
    47
{
slouken@172
    48
	IOHIDElementCookie cookie;				// unique value which identifies element, will NOT change
slouken@172
    49
	long min;								// reported min value possible
slouken@172
    50
	long max;								// reported max value possible
slouken@172
    51
/*
slouken@172
    52
	TODO: maybe should handle the following stuff somehow?
slouken@172
    53
slouken@172
    54
	long scaledMin;							// reported scaled min value possible
slouken@172
    55
	long scaledMax;							// reported scaled max value possible
slouken@172
    56
	long size;								// size in bits of data return from element
slouken@172
    57
	Boolean relative;						// are reports relative to last report (deltas)
slouken@172
    58
	Boolean wrapping;						// does element wrap around (one value higher than max is min)
slouken@172
    59
	Boolean nonLinear;						// are the values reported non-linear relative to element movement
slouken@172
    60
	Boolean preferredState;					// does element have a preferred state (such as a button)
slouken@172
    61
	Boolean nullState;						// does element have null state
slouken@172
    62
*/
slouken@172
    63
slouken@172
    64
	/* runtime variables used for auto-calibration */
slouken@172
    65
	long minReport;							// min returned value
slouken@172
    66
	long maxReport;							// max returned value
slouken@172
    67
	
slouken@172
    68
	struct recElement * pNext;				// next element in list
slouken@172
    69
};
slouken@172
    70
typedef struct recElement recElement;
slouken@172
    71
slouken@172
    72
struct joystick_hwdata
slouken@172
    73
{
slouken@172
    74
	IOHIDDeviceInterface ** interface;		// interface to device, NULL = no interface
slouken@172
    75
slouken@172
    76
	char product[256];							// name of product
slouken@172
    77
	long usage;								// usage page from IOUSBHID Parser.h which defines general usage
slouken@172
    78
	long usagePage;							// usage within above page from IOUSBHID Parser.h which defines specific usage
slouken@172
    79
slouken@172
    80
	long axes;								// number of axis (calculated, not reported by device)
slouken@172
    81
	long buttons;							// number of buttons (calculated, not reported by device)
slouken@172
    82
	long hats;								// number of hat switches (calculated, not reported by device)
slouken@172
    83
	long elements;							// number of total elements (shouldbe total of above) (calculated, not reported by device)
slouken@172
    84
slouken@172
    85
	recElement* firstAxis;
slouken@172
    86
	recElement* firstButton;
slouken@172
    87
	recElement* firstHat;
slouken@172
    88
slouken@172
    89
	struct recDevice* pNext;				// next device
slouken@172
    90
};
slouken@172
    91
typedef struct joystick_hwdata recDevice;
slouken@172
    92
slouken@172
    93
slouken@172
    94
/* Linked list of all available devices */
slouken@172
    95
static recDevice *gpDeviceList = NULL;
slouken@172
    96
slouken@172
    97
slouken@172
    98
void HIDReportErrorNum (char * strError, long numError)
slouken@172
    99
{
slouken@172
   100
	SDL_SetError(strError);
slouken@172
   101
}
slouken@172
   102
slouken@172
   103
static void HIDGetCollectionElements (CFMutableDictionaryRef deviceProperties, recDevice *pDevice);
slouken@172
   104
slouken@172
   105
/* returns current value for element, polling element
slouken@172
   106
 * will return 0 on error conditions which should be accounted for by application
slouken@172
   107
 */
slouken@172
   108
slouken@172
   109
SInt32 HIDGetElementValue (recDevice *pDevice, recElement *pElement)
slouken@172
   110
{
slouken@172
   111
	IOReturn result = kIOReturnSuccess;
slouken@172
   112
	IOHIDEventStruct hidEvent;
slouken@172
   113
	hidEvent.value = 0;
slouken@172
   114
	
slouken@172
   115
	if (NULL != pDevice && NULL != pElement && NULL != pDevice->interface)
slouken@172
   116
	{
slouken@172
   117
		result = (*(pDevice->interface))->getElementValue(pDevice->interface, pElement->cookie, &hidEvent);
slouken@172
   118
		if (kIOReturnSuccess == result)
slouken@172
   119
		{
slouken@172
   120
			/* record min and max for auto calibration */
slouken@172
   121
			if (hidEvent.value < pElement->minReport)
slouken@172
   122
				pElement->minReport = hidEvent.value;
slouken@172
   123
			if (hidEvent.value > pElement->maxReport)
slouken@172
   124
				pElement->maxReport = hidEvent.value;
slouken@172
   125
		}
slouken@172
   126
	}
slouken@172
   127
slouken@172
   128
	// auto user scale
slouken@172
   129
	return hidEvent.value;
slouken@172
   130
}
slouken@172
   131
slouken@172
   132
/* similiar to HIDGetElementValue, but auto-calibrates the value before returning it */
slouken@172
   133
slouken@172
   134
SInt32 HIDCalibratedValue (recDevice *pDevice, recElement *pElement)
slouken@172
   135
{
slouken@172
   136
	float deviceScale = pElement->max - pElement->min;
slouken@172
   137
	float readScale = pElement->maxReport - pElement->minReport;
slouken@172
   138
	SInt32 value = HIDGetElementValue(pDevice, pElement);
slouken@172
   139
	if (readScale == 0)
slouken@172
   140
		return value; // no scaling at all
slouken@172
   141
	else
slouken@172
   142
		return ((value - pElement->minReport) * deviceScale / readScale) + pElement->min;
slouken@172
   143
}
slouken@172
   144
slouken@172
   145
/* similiar to HIDCalibratedValue but calibrates to an arbitrary scale instead of the elements default scale */
slouken@172
   146
slouken@172
   147
SInt32 HIDScaledCalibratedValue (recDevice *pDevice, recElement *pElement, long min, long max)
slouken@172
   148
{
slouken@172
   149
	float deviceScale = max - min;
slouken@172
   150
	float readScale = pElement->maxReport - pElement->minReport;
slouken@172
   151
	SInt32 value = HIDGetElementValue(pDevice, pElement);
slouken@172
   152
	if (readScale == 0)
slouken@172
   153
		return value; // no scaling at all
slouken@172
   154
	else
slouken@172
   155
		return ((value - pElement->minReport) * deviceScale / readScale) + min;
slouken@172
   156
}
slouken@172
   157
slouken@172
   158
/* Create and open an interface to device, required prior to extracting values or building queues.
slouken@172
   159
 * Note: appliction now owns the device and must close and release it prior to exiting
slouken@172
   160
 */
slouken@172
   161
slouken@172
   162
IOReturn HIDCreateOpenDeviceInterface (io_object_t hidDevice, recDevice *pDevice)
slouken@172
   163
{
slouken@172
   164
	IOReturn result = kIOReturnSuccess;
slouken@172
   165
	HRESULT plugInResult = S_OK;
slouken@172
   166
	SInt32 score = 0;
slouken@172
   167
	IOCFPlugInInterface ** ppPlugInInterface = NULL;
slouken@172
   168
	
slouken@172
   169
	if (NULL == pDevice->interface)
slouken@172
   170
	{
slouken@172
   171
		result = IOCreatePlugInInterfaceForService (hidDevice, kIOHIDDeviceUserClientTypeID,
slouken@172
   172
													kIOCFPlugInInterfaceID, &ppPlugInInterface, &score);
slouken@172
   173
		if (kIOReturnSuccess == result)
slouken@172
   174
		{
slouken@172
   175
			// Call a method of the intermediate plug-in to create the device interface
slouken@172
   176
			plugInResult = (*ppPlugInInterface)->QueryInterface (ppPlugInInterface,
slouken@172
   177
								CFUUIDGetUUIDBytes (kIOHIDDeviceInterfaceID), (void *) &(pDevice->interface));
slouken@172
   178
			if (S_OK != plugInResult)
slouken@172
   179
				HIDReportErrorNum ("CouldnŐt query HID class device interface from plugInInterface", plugInResult);
slouken@172
   180
			(*ppPlugInInterface)->Release (ppPlugInInterface);
slouken@172
   181
		}
slouken@172
   182
		else
slouken@172
   183
			HIDReportErrorNum ("Failed to create **plugInInterface via IOCreatePlugInInterfaceForService.", result);
slouken@172
   184
	}
slouken@172
   185
	if (NULL != pDevice->interface)
slouken@172
   186
	{
slouken@172
   187
		result = (*(pDevice->interface))->open (pDevice->interface, 0);
slouken@172
   188
		if (kIOReturnSuccess != result)
slouken@172
   189
			HIDReportErrorNum ("Failed to open pDevice->interface via open.", result);
slouken@172
   190
	}
slouken@172
   191
	return result;
slouken@172
   192
}
slouken@172
   193
slouken@172
   194
/* Closes and releases interface to device, should be done prior to exting application
slouken@172
   195
 * Note: will have no affect if device or interface do not exist
slouken@172
   196
 * application will "own" the device if interface is not closed
slouken@172
   197
 * (device may have to be plug and re-plugged in different location to get it working again without a restart)
slouken@172
   198
 */
slouken@172
   199
slouken@172
   200
IOReturn HIDCloseReleaseInterface (recDevice *pDevice)
slouken@172
   201
{
slouken@172
   202
	IOReturn result = kIOReturnSuccess;
slouken@172
   203
	
slouken@172
   204
	if ((NULL != pDevice) && (NULL != pDevice->interface))
slouken@172
   205
	{
slouken@172
   206
		// close the interface
slouken@172
   207
		result = (*(pDevice->interface))->close (pDevice->interface);
slouken@172
   208
		if (kIOReturnNotOpen == result)
slouken@172
   209
		{
slouken@172
   210
			//  do nothing as device was not opened, thus can't be closed
slouken@172
   211
		}
slouken@172
   212
		else if (kIOReturnSuccess != result)
slouken@172
   213
			HIDReportErrorNum ("Failed to close IOHIDDeviceInterface.", result);
slouken@172
   214
		//release the interface
slouken@172
   215
		result = (*(pDevice->interface))->Release (pDevice->interface);
slouken@172
   216
		if (kIOReturnSuccess != result)
slouken@172
   217
			HIDReportErrorNum ("Failed to release IOHIDDeviceInterface.", result);
slouken@172
   218
		pDevice->interface = NULL;
slouken@172
   219
	}	
slouken@172
   220
	return result;
slouken@172
   221
}
slouken@172
   222
slouken@172
   223
/* extracts actual specific element information from each element CF dictionary entry */
slouken@172
   224
slouken@172
   225
static void HIDGetElementInfo (CFTypeRef refElement, recElement *pElement)
slouken@172
   226
{
slouken@172
   227
	long number;
slouken@172
   228
	CFTypeRef refType;
slouken@172
   229
slouken@172
   230
	refType = CFDictionaryGetValue (refElement, CFSTR(kIOHIDElementCookieKey));
slouken@172
   231
	if (refType && CFNumberGetValue (refType, kCFNumberLongType, &number))
slouken@172
   232
		pElement->cookie = (IOHIDElementCookie) number;
slouken@172
   233
	refType = CFDictionaryGetValue (refElement, CFSTR(kIOHIDElementMinKey));
slouken@172
   234
	if (refType && CFNumberGetValue (refType, kCFNumberLongType, &number))
slouken@172
   235
		pElement->min = number;
slouken@172
   236
		pElement->maxReport = pElement->min;
slouken@172
   237
	refType = CFDictionaryGetValue (refElement, CFSTR(kIOHIDElementMaxKey));
slouken@172
   238
	if (refType && CFNumberGetValue (refType, kCFNumberLongType, &number))
slouken@172
   239
		pElement->max = number;
slouken@172
   240
		pElement->minReport = pElement->max;
slouken@172
   241
/*
slouken@172
   242
	TODO: maybe should handle the following stuff somehow?
slouken@172
   243
slouken@172
   244
	refType = CFDictionaryGetValue (refElement, CFSTR(kIOHIDElementScaledMinKey));
slouken@172
   245
	if (refType && CFNumberGetValue (refType, kCFNumberLongType, &number))
slouken@172
   246
		pElement->scaledMin = number;
slouken@172
   247
	refType = CFDictionaryGetValue (refElement, CFSTR(kIOHIDElementScaledMaxKey));
slouken@172
   248
	if (refType && CFNumberGetValue (refType, kCFNumberLongType, &number))
slouken@172
   249
		pElement->scaledMax = number;
slouken@172
   250
	refType = CFDictionaryGetValue (refElement, CFSTR(kIOHIDElementSizeKey));
slouken@172
   251
	if (refType && CFNumberGetValue (refType, kCFNumberLongType, &number))
slouken@172
   252
		pElement->size = number;
slouken@172
   253
	refType = CFDictionaryGetValue (refElement, CFSTR(kIOHIDElementIsRelativeKey));
slouken@172
   254
	if (refType)
slouken@172
   255
		pElement->relative = CFBooleanGetValue (refType);
slouken@172
   256
	refType = CFDictionaryGetValue (refElement, CFSTR(kIOHIDElementIsWrappingKey));
slouken@172
   257
	if (refType)
slouken@172
   258
		pElement->wrapping = CFBooleanGetValue (refType);
slouken@172
   259
	refType = CFDictionaryGetValue (refElement, CFSTR(kIOHIDElementIsNonLinearKey));
slouken@172
   260
	if (refType)
slouken@172
   261
		pElement->nonLinear = CFBooleanGetValue (refType);
slouken@172
   262
	refType = CFDictionaryGetValue (refElement, CFSTR(kIOHIDElementHasPreferedStateKey));
slouken@172
   263
	if (refType)
slouken@172
   264
		pElement->preferredState = CFBooleanGetValue (refType);
slouken@172
   265
	refType = CFDictionaryGetValue (refElement, CFSTR(kIOHIDElementHasNullStateKey));
slouken@172
   266
	if (refType)
slouken@172
   267
		pElement->nullState = CFBooleanGetValue (refType);
slouken@172
   268
*/
slouken@172
   269
}			
slouken@172
   270
slouken@172
   271
/* examines CF dictionary vlaue in device element hierarchy to determine if it is element of interest or a collection of more elements
slouken@172
   272
 * if element of interest allocate storage, add to list and retrieve element specific info
slouken@172
   273
 * if collection then pass on to deconstruction collection into additional individual elements
slouken@172
   274
 */
slouken@172
   275
slouken@172
   276
static void HIDAddElement (CFTypeRef refElement, recDevice* pDevice)
slouken@172
   277
{
slouken@172
   278
	recElement* element = NULL;
slouken@172
   279
	recElement** headElement = NULL;
slouken@172
   280
	long elementType, usagePage, usage;
slouken@172
   281
	CFTypeRef refElementType = CFDictionaryGetValue (refElement, CFSTR(kIOHIDElementTypeKey));
slouken@172
   282
	CFTypeRef refUsagePage = CFDictionaryGetValue (refElement, CFSTR(kIOHIDElementUsagePageKey));
slouken@172
   283
	CFTypeRef refUsage = CFDictionaryGetValue (refElement, CFSTR(kIOHIDElementUsageKey));
slouken@172
   284
slouken@172
   285
slouken@172
   286
	if ((refElementType) && (CFNumberGetValue (refElementType, kCFNumberLongType, &elementType)))
slouken@172
   287
	{
slouken@172
   288
		/* look at types of interest */
slouken@172
   289
		if ((elementType == kIOHIDElementTypeInput_Misc) || (elementType == kIOHIDElementTypeInput_Button) ||
slouken@172
   290
			(elementType == kIOHIDElementTypeInput_Axis))
slouken@172
   291
		{
slouken@172
   292
			if (refUsagePage && CFNumberGetValue (refUsagePage, kCFNumberLongType, &usagePage) &&
slouken@172
   293
				refUsage && CFNumberGetValue (refUsage, kCFNumberLongType, &usage))
slouken@172
   294
			{
slouken@172
   295
				switch (usagePage) /* only interested in kHIDPage_GenericDesktop and kHIDPage_Button */
slouken@172
   296
				{
slouken@172
   297
					case kHIDPage_GenericDesktop:
slouken@172
   298
						{
slouken@172
   299
							switch (usage) /* look at usage to determine function */
slouken@172
   300
							{
slouken@172
   301
								case kHIDUsage_GD_X:
slouken@172
   302
								case kHIDUsage_GD_Y:
slouken@172
   303
								case kHIDUsage_GD_Z:
slouken@172
   304
								case kHIDUsage_GD_Rx:
slouken@172
   305
								case kHIDUsage_GD_Ry:
slouken@172
   306
								case kHIDUsage_GD_Rz:
slouken@172
   307
									element = (recElement *) NewPtrClear (sizeof (recElement));
slouken@172
   308
									if (element)
slouken@172
   309
									{
slouken@172
   310
										pDevice->axes++;
slouken@172
   311
										headElement = &(pDevice->firstAxis);
slouken@172
   312
									}
slouken@172
   313
								break;
slouken@172
   314
								case kHIDUsage_GD_Hatswitch:
slouken@172
   315
									element = (recElement *) NewPtrClear (sizeof (recElement));
slouken@172
   316
									if (element)
slouken@172
   317
									{
slouken@172
   318
										pDevice->hats++;
slouken@172
   319
										headElement = &(pDevice->firstHat);
slouken@172
   320
									}
slouken@172
   321
								break;
slouken@172
   322
							}							
slouken@172
   323
						}
slouken@172
   324
						break;
slouken@172
   325
					case kHIDPage_Button:
slouken@172
   326
						element = (recElement *) NewPtrClear (sizeof (recElement));
slouken@172
   327
						if (element)
slouken@172
   328
						{
slouken@172
   329
							pDevice->buttons++;
slouken@172
   330
							headElement = &(pDevice->firstButton);
slouken@172
   331
						}
slouken@172
   332
						break;
slouken@172
   333
					default:
slouken@172
   334
						break;
slouken@172
   335
				}
slouken@172
   336
			}
slouken@172
   337
		}
slouken@172
   338
		else if (kIOHIDElementTypeCollection == elementType)
slouken@172
   339
			HIDGetCollectionElements ((CFMutableDictionaryRef) refElement, pDevice);
slouken@172
   340
	}
slouken@172
   341
slouken@172
   342
	if (element && headElement) /* add to list */
slouken@172
   343
	{
slouken@172
   344
		pDevice->elements++;
slouken@172
   345
		if (NULL == *headElement)
slouken@172
   346
			*headElement = element;
slouken@172
   347
		else
slouken@172
   348
		{
slouken@172
   349
			recElement *elementPrevious, *elementCurrent;
slouken@172
   350
			elementCurrent = *headElement;
slouken@172
   351
			while (elementCurrent)
slouken@172
   352
			{
slouken@172
   353
				elementPrevious = elementCurrent;
slouken@172
   354
				elementCurrent = elementPrevious->pNext;
slouken@172
   355
			}
slouken@172
   356
			elementPrevious->pNext = element;
slouken@172
   357
		}
slouken@172
   358
		element->pNext = NULL;
slouken@172
   359
		HIDGetElementInfo (refElement, element);
slouken@172
   360
	}
slouken@172
   361
}
slouken@172
   362
slouken@172
   363
/* collects information from each array member in device element list (each array memeber = element) */
slouken@172
   364
slouken@172
   365
static void HIDGetElementsCFArrayHandler (const void * value, void * parameter)
slouken@172
   366
{
slouken@172
   367
	if (CFGetTypeID (value) == CFDictionaryGetTypeID ())
slouken@172
   368
		HIDAddElement ((CFTypeRef) value, (recDevice *) parameter);
slouken@172
   369
}
slouken@172
   370
slouken@172
   371
/* handles retrieval of element information from arrays of elements in device IO registry information */
slouken@172
   372
slouken@172
   373
static void HIDGetElements (CFTypeRef refElementCurrent, recDevice *pDevice)
slouken@172
   374
{
slouken@172
   375
	CFTypeID type = CFGetTypeID (refElementCurrent);
slouken@172
   376
	if (type == CFArrayGetTypeID()) /* if element is an array */
slouken@172
   377
	{
slouken@172
   378
		CFRange range = {0, CFArrayGetCount (refElementCurrent)};
slouken@172
   379
		/* CountElementsCFArrayHandler called for each array member */
slouken@172
   380
		CFArrayApplyFunction (refElementCurrent, range, HIDGetElementsCFArrayHandler, pDevice);
slouken@172
   381
	}
slouken@172
   382
}			
slouken@172
   383
slouken@172
   384
/* handles extracting element information from element collection CF types
slouken@172
   385
 * used from top level element decoding and hierarchy deconstruction to flatten device element list
slouken@172
   386
 */
slouken@172
   387
slouken@172
   388
static void HIDGetCollectionElements (CFMutableDictionaryRef deviceProperties, recDevice *pDevice)
slouken@172
   389
{
slouken@172
   390
	CFTypeRef refElementTop = CFDictionaryGetValue (deviceProperties, CFSTR(kIOHIDElementKey));
slouken@172
   391
	if (refElementTop)
slouken@172
   392
		HIDGetElements (refElementTop, pDevice);
slouken@172
   393
}
slouken@172
   394
slouken@172
   395
/* use top level element usage page and usage to discern device usage page and usage setting appropriate vlaues in device record */
slouken@172
   396
slouken@172
   397
static void HIDTopLevelElementHandler (const void * value, void * parameter)
slouken@172
   398
{
slouken@172
   399
	CFTypeRef refCF = 0;
slouken@172
   400
	if (CFGetTypeID (value) != CFDictionaryGetTypeID ())
slouken@172
   401
		return;
slouken@172
   402
	refCF = CFDictionaryGetValue (value, CFSTR(kIOHIDElementUsagePageKey));
slouken@172
   403
	if (!CFNumberGetValue (refCF, kCFNumberLongType, &((recDevice *) parameter)->usagePage))
slouken@172
   404
		SDL_SetError ("CFNumberGetValue error retrieving pDevice->usagePage.");
slouken@172
   405
	refCF = CFDictionaryGetValue (value, CFSTR(kIOHIDElementUsageKey));
slouken@172
   406
	if (!CFNumberGetValue (refCF, kCFNumberLongType, &((recDevice *) parameter)->usage))
slouken@172
   407
		SDL_SetError ("CFNumberGetValue error retrieving pDevice->usage.");
slouken@172
   408
}
slouken@172
   409
slouken@172
   410
/* extracts device info from CF dictionary records in IO registry */
slouken@172
   411
slouken@172
   412
static void HIDGetDeviceInfo (io_object_t hidDevice, CFMutableDictionaryRef hidProperties, recDevice *pDevice)
slouken@172
   413
{
slouken@172
   414
	CFMutableDictionaryRef usbProperties = 0;
slouken@172
   415
	io_registry_entry_t parent1, parent2;
slouken@172
   416
	
slouken@172
   417
	/* Mac OS X currently is not mirroring all USB properties to HID page so need to look at USB device page also
slouken@172
   418
	 * get dictionary for usb properties: step up two levels and get CF dictionary for USB properties
slouken@172
   419
	 */
slouken@172
   420
	if ((KERN_SUCCESS == IORegistryEntryGetParentEntry (hidDevice, kIOServicePlane, &parent1)) &&
slouken@172
   421
		(KERN_SUCCESS == IORegistryEntryGetParentEntry (parent1, kIOServicePlane, &parent2)) &&
slouken@172
   422
		(KERN_SUCCESS == IORegistryEntryCreateCFProperties (parent2, &usbProperties, kCFAllocatorDefault, kNilOptions)))
slouken@172
   423
	{
slouken@172
   424
		if (usbProperties)
slouken@172
   425
		{
slouken@172
   426
			CFTypeRef refCF = 0;
slouken@172
   427
			/* get device info
slouken@172
   428
			 * try hid dictionary first, if fail then go to usb dictionary
slouken@172
   429
			 */
slouken@172
   430
			
slouken@172
   431
			
slouken@172
   432
			/* get product name */
slouken@172
   433
			refCF = CFDictionaryGetValue (hidProperties, CFSTR(kIOHIDProductKey));
slouken@172
   434
			if (!refCF)
slouken@172
   435
				refCF = CFDictionaryGetValue (usbProperties, CFSTR("USB Product Name"));
slouken@172
   436
			if (refCF)
slouken@172
   437
			{
slouken@172
   438
				if (!CFStringGetCString (refCF, pDevice->product, 256, CFStringGetSystemEncoding ()))
slouken@172
   439
					SDL_SetError ("CFStringGetCString error retrieving pDevice->product.");
slouken@172
   440
			}
slouken@172
   441
			
slouken@172
   442
			/* get usage page and usage */
slouken@172
   443
			refCF = CFDictionaryGetValue (hidProperties, CFSTR(kIOHIDPrimaryUsagePageKey));
slouken@172
   444
			if (refCF)
slouken@172
   445
			{
slouken@172
   446
				if (!CFNumberGetValue (refCF, kCFNumberLongType, &pDevice->usagePage))
slouken@172
   447
					SDL_SetError ("CFNumberGetValue error retrieving pDevice->usagePage.");
slouken@172
   448
				refCF = CFDictionaryGetValue (hidProperties, CFSTR(kIOHIDPrimaryUsageKey));
slouken@172
   449
				if (refCF)
slouken@172
   450
					if (!CFNumberGetValue (refCF, kCFNumberLongType, &pDevice->usage))
slouken@172
   451
						SDL_SetError ("CFNumberGetValue error retrieving pDevice->usage.");
slouken@172
   452
			}
slouken@172
   453
slouken@172
   454
			if (NULL == refCF) /* get top level element HID usage page or usage */
slouken@172
   455
			{
slouken@172
   456
				/* use top level element instead */
slouken@172
   457
				CFTypeRef refCFTopElement = 0;
slouken@172
   458
				refCFTopElement = CFDictionaryGetValue (hidProperties, CFSTR(kIOHIDElementKey));
slouken@172
   459
				{
slouken@172
   460
					/* refCFTopElement points to an array of element dictionaries */
slouken@172
   461
					CFRange range = {0, CFArrayGetCount (refCFTopElement)};
slouken@172
   462
					CFArrayApplyFunction (refCFTopElement, range, HIDTopLevelElementHandler, pDevice);
slouken@172
   463
				}
slouken@172
   464
			}
slouken@172
   465
slouken@172
   466
			CFRelease (usbProperties);
slouken@172
   467
		}
slouken@172
   468
		else
slouken@172
   469
			SDL_SetError ("IORegistryEntryCreateCFProperties failed to create usbProperties.");
slouken@172
   470
slouken@172
   471
		if (kIOReturnSuccess != IOObjectRelease (parent2))
slouken@172
   472
			SDL_SetError ("IOObjectRelease error with parent2.");
slouken@172
   473
		if (kIOReturnSuccess != IOObjectRelease (parent1))
slouken@172
   474
			SDL_SetError ("IOObjectRelease error with parent1.");
slouken@172
   475
	}
slouken@172
   476
}
slouken@172
   477
slouken@172
   478
slouken@172
   479
static recDevice *HIDBuildDevice (io_object_t hidDevice)
slouken@172
   480
{
slouken@172
   481
	recDevice *pDevice = (recDevice *) NewPtrClear (sizeof (recDevice));
slouken@172
   482
	if (pDevice)
slouken@172
   483
	{
slouken@172
   484
		/* get dictionary for HID properties */
slouken@172
   485
		CFMutableDictionaryRef hidProperties = 0;
slouken@172
   486
		kern_return_t result = IORegistryEntryCreateCFProperties (hidDevice, &hidProperties, kCFAllocatorDefault, kNilOptions);
slouken@172
   487
		if ((result == KERN_SUCCESS) && hidProperties)
slouken@172
   488
		{
slouken@172
   489
			/* create device interface */
slouken@172
   490
			result = HIDCreateOpenDeviceInterface (hidDevice, pDevice);
slouken@172
   491
			if (kIOReturnSuccess == result)
slouken@172
   492
			{
slouken@172
   493
				HIDGetDeviceInfo (hidDevice, hidProperties, pDevice); /* hidDevice used to find parents in registry tree */
slouken@172
   494
				HIDGetCollectionElements (hidProperties, pDevice);
slouken@172
   495
			}
slouken@172
   496
			else
slouken@172
   497
			{
slouken@172
   498
				DisposePtr(pDevice);
slouken@172
   499
				pDevice = NULL;
slouken@172
   500
			}
slouken@172
   501
			CFRelease (hidProperties);
slouken@172
   502
		}
slouken@172
   503
		else
slouken@172
   504
		{
slouken@172
   505
			DisposePtr(pDevice);
slouken@172
   506
			pDevice = NULL;
slouken@172
   507
		}
slouken@172
   508
	}
slouken@172
   509
	return pDevice;
slouken@172
   510
}
slouken@172
   511
slouken@172
   512
/* disposes of the element list associated with a device and the memory associated with the list
slouken@172
   513
 */
slouken@172
   514
slouken@172
   515
static void HIDDisposeElementList (recElement **elementList)
slouken@172
   516
{
slouken@172
   517
	recElement *pElement = *elementList;
slouken@172
   518
	while (pElement)
slouken@172
   519
	{
slouken@172
   520
		recElement *pElementNext = pElement->pNext;
slouken@172
   521
		DisposePtr ((Ptr) pElement);
slouken@172
   522
		pElement = pElementNext;
slouken@172
   523
	}
slouken@172
   524
	*elementList = NULL;
slouken@172
   525
}
slouken@172
   526
slouken@172
   527
/* disposes of a single device, closing and releaseing interface, freeing memory fro device and elements, setting device pointer to NULL
slouken@172
   528
 * all your device no longer belong to us... (i.e., you do not 'own' the device anymore)
slouken@172
   529
 */
slouken@172
   530
slouken@172
   531
static recDevice *HIDDisposeDevice (recDevice **ppDevice)
slouken@172
   532
{
slouken@172
   533
	kern_return_t result = KERN_SUCCESS;
slouken@172
   534
	recDevice *pDeviceNext = NULL;
slouken@172
   535
	if (*ppDevice)
slouken@172
   536
	{
slouken@172
   537
		// save next device prior to disposing of this device
slouken@172
   538
		pDeviceNext = (*ppDevice)->pNext;
slouken@172
   539
		
slouken@172
   540
		/* free element lists */
slouken@172
   541
		HIDDisposeElementList (&(*ppDevice)->firstAxis);
slouken@172
   542
		HIDDisposeElementList (&(*ppDevice)->firstButton);
slouken@172
   543
		HIDDisposeElementList (&(*ppDevice)->firstHat);
slouken@172
   544
		
slouken@172
   545
		result = HIDCloseReleaseInterface (*ppDevice); /* function sanity checks interface value (now application does not own device) */
slouken@172
   546
		if (kIOReturnSuccess != result)
slouken@172
   547
			HIDReportErrorNum ("HIDCloseReleaseInterface failed when trying to dipose device.", result);
slouken@172
   548
		DisposePtr ((Ptr)*ppDevice);
slouken@172
   549
		*ppDevice = NULL;
slouken@172
   550
	}
slouken@172
   551
	return pDeviceNext;
slouken@172
   552
}
slouken@172
   553
slouken@172
   554
slouken@172
   555
/* Function to scan the system for joysticks.
slouken@172
   556
 * Joystick 0 should be the system default joystick.
slouken@172
   557
 * This function should return the number of available joysticks, or -1
slouken@172
   558
 * on an unrecoverable fatal error.
slouken@172
   559
 */
slouken@172
   560
int SDL_SYS_JoystickInit(void)
slouken@172
   561
{
slouken@172
   562
	IOReturn result = kIOReturnSuccess;
slouken@172
   563
	mach_port_t masterPort = NULL;
slouken@172
   564
	io_iterator_t hidObjectIterator = NULL;
slouken@172
   565
	CFMutableDictionaryRef hidMatchDictionary = NULL;
slouken@172
   566
	recDevice *device, *lastDevice;
slouken@172
   567
	io_object_t ioHIDDeviceObject = NULL;
slouken@172
   568
	UInt32 usagePage = kHIDPage_GenericDesktop;
slouken@172
   569
	UInt32 usage = kHIDUsage_GD_Joystick; /* We probably also should check for gamepads? */
slouken@172
   570
	
slouken@172
   571
	SDL_numjoysticks = 0;
slouken@172
   572
	
slouken@172
   573
	if (NULL != gpDeviceList)
slouken@172
   574
	{
slouken@172
   575
		SDL_SetError("Joystick: Device list already inited.");
slouken@172
   576
		return -1;
slouken@172
   577
	}
slouken@172
   578
	
slouken@172
   579
	result = IOMasterPort (bootstrap_port, &masterPort);
slouken@172
   580
	if (kIOReturnSuccess != result)
slouken@172
   581
	{
slouken@172
   582
		SDL_SetError("Joystick: IOMasterPort error with bootstrap_port.");
slouken@172
   583
		return -1;
slouken@172
   584
	}
slouken@172
   585
slouken@172
   586
	/* Set up a matching dictionary to search I/O Registry by class name for all HID class devices. */
slouken@172
   587
	hidMatchDictionary = IOServiceMatching (kIOHIDDeviceKey);
slouken@172
   588
	if ((hidMatchDictionary != NULL) && (usagePage) && (usage))
slouken@172
   589
	{
slouken@172
   590
		/* Add key for device type (joystick, in this case) to refine the matching dictionary. */
slouken@172
   591
		CFNumberRef refUsage = NULL, refUsagePage = NULL;
slouken@172
   592
slouken@172
   593
		refUsage = CFNumberCreate (kCFAllocatorDefault, kCFNumberIntType, &usagePage);
slouken@172
   594
		CFDictionarySetValue (hidMatchDictionary, CFSTR (kIOHIDPrimaryUsageKey), refUsage);
slouken@172
   595
		refUsagePage = CFNumberCreate (kCFAllocatorDefault, kCFNumberIntType, &usage);
slouken@172
   596
		CFDictionarySetValue (hidMatchDictionary, CFSTR (kIOHIDPrimaryUsagePageKey), refUsagePage);
slouken@172
   597
	}
slouken@172
   598
	else
slouken@172
   599
	{
slouken@172
   600
		SDL_SetError("Joystick: Failed to get HID CFMutableDictionaryRef via IOServiceMatching.");
slouken@172
   601
		return -1;
slouken@172
   602
	}
slouken@172
   603
	
slouken@172
   604
	/*/ Now search I/O Registry for matching devices. */
slouken@172
   605
	result = IOServiceGetMatchingServices (masterPort, hidMatchDictionary, &hidObjectIterator);
slouken@172
   606
	/* Check for errors */
slouken@172
   607
	if ((kIOReturnSuccess != result) || (NULL == hidObjectIterator))
slouken@172
   608
	{
slouken@172
   609
		SDL_SetError("Joystick: Couldn't create a HID object iterator.");
slouken@172
   610
		return -1;
slouken@172
   611
	}
slouken@172
   612
	/* IOServiceGetMatchingServices consumes a reference to the dictionary, so we don't need to release the dictionary ref. */
slouken@172
   613
slouken@172
   614
	/* build flat linked list of devices from device iterator */
slouken@172
   615
slouken@172
   616
	gpDeviceList = lastDevice = NULL;
slouken@172
   617
	
slouken@172
   618
	while ((ioHIDDeviceObject = IOIteratorNext (hidObjectIterator)))
slouken@172
   619
	{
slouken@172
   620
		/* build a device record */
slouken@172
   621
		device = HIDBuildDevice (ioHIDDeviceObject);
slouken@172
   622
		if (!device)
slouken@172
   623
			continue;
slouken@172
   624
slouken@172
   625
		/* dump device object, it is no longer needed */
slouken@172
   626
		result = IOObjectRelease (ioHIDDeviceObject);
slouken@172
   627
//		if (KERN_SUCCESS != result)
slouken@172
   628
//			HIDReportErrorNum ("IOObjectRelease error with ioHIDDeviceObject.", result);
slouken@172
   629
		/* Add device to the end of the list */
slouken@172
   630
		if (lastDevice)
slouken@172
   631
			lastDevice->pNext = device;
slouken@172
   632
		else
slouken@172
   633
			gpDeviceList = device;
slouken@172
   634
		lastDevice = device;
slouken@172
   635
	}
slouken@172
   636
	result = IOObjectRelease (hidObjectIterator); /* release the iterator */
slouken@172
   637
slouken@172
   638
	/* Count the total number of devices we found */
slouken@172
   639
	device = gpDeviceList;
slouken@172
   640
	while (device)
slouken@172
   641
	{
slouken@172
   642
		SDL_numjoysticks++;
slouken@172
   643
		device = device->pNext;
slouken@172
   644
	}
slouken@172
   645
	
slouken@172
   646
	return SDL_numjoysticks;
slouken@172
   647
}
slouken@172
   648
slouken@172
   649
/* Function to get the device-dependent name of a joystick */
slouken@172
   650
const char *SDL_SYS_JoystickName(int index)
slouken@172
   651
{
slouken@172
   652
	recDevice *device = gpDeviceList;
slouken@172
   653
	
slouken@172
   654
	for (; index > 0; index--)
slouken@172
   655
		device = device->pNext;
slouken@172
   656
slouken@172
   657
	return device->product;
slouken@172
   658
}
slouken@172
   659
slouken@172
   660
/* Function to open a joystick for use.
slouken@172
   661
 * The joystick to open is specified by the index field of the joystick.
slouken@172
   662
 * This should fill the nbuttons and naxes fields of the joystick structure.
slouken@172
   663
 * It returns 0, or -1 if there is an error.
slouken@172
   664
 */
slouken@172
   665
int SDL_SYS_JoystickOpen(SDL_Joystick *joystick)
slouken@172
   666
{
slouken@172
   667
	recDevice *device = gpDeviceList;
slouken@172
   668
	int index;
slouken@172
   669
	
slouken@172
   670
	for (index = joystick->index; index > 0; index--)
slouken@172
   671
		device = device->pNext;
slouken@172
   672
slouken@172
   673
	joystick->hwdata = device;
slouken@172
   674
	joystick->name = device->product;
slouken@172
   675
slouken@172
   676
	joystick->naxes = device->axes;
slouken@172
   677
	joystick->nhats = device->hats;
slouken@172
   678
	joystick->nballs = 0;
slouken@172
   679
	joystick->nbuttons = device->buttons;
slouken@172
   680
slouken@172
   681
	return 0;
slouken@172
   682
}
slouken@172
   683
slouken@172
   684
/* Function to update the state of a joystick - called as a device poll.
slouken@172
   685
 * This function shouldn't update the joystick structure directly,
slouken@172
   686
 * but instead should call SDL_PrivateJoystick*() to deliver events
slouken@172
   687
 * and update joystick device state.
slouken@172
   688
 */
slouken@172
   689
void SDL_SYS_JoystickUpdate(SDL_Joystick *joystick)
slouken@172
   690
{
slouken@172
   691
	recDevice *device = joystick->hwdata;
slouken@172
   692
	recElement *element;
slouken@172
   693
	SInt32 value;
slouken@172
   694
	int i;
slouken@172
   695
	
slouken@172
   696
	element = device->firstAxis;
slouken@172
   697
	i = 0;
slouken@172
   698
	while (element)
slouken@172
   699
	{
slouken@172
   700
		value = HIDScaledCalibratedValue(device, element, -32768, 32767);
slouken@172
   701
		if ( value != joystick->axes[i] )
slouken@172
   702
			SDL_PrivateJoystickAxis(joystick, i, value);
slouken@172
   703
		element = element->pNext;
slouken@172
   704
		++i;
slouken@172
   705
	}
slouken@172
   706
	
slouken@172
   707
	element = device->firstButton;
slouken@172
   708
	i = 0;
slouken@172
   709
	while (element)
slouken@172
   710
	{
slouken@172
   711
		value = HIDGetElementValue(device, element);
slouken@172
   712
		if ( value != joystick->buttons[i] )
slouken@172
   713
			SDL_PrivateJoystickButton(joystick, i, value);
slouken@172
   714
		element = element->pNext;
slouken@172
   715
		++i;
slouken@172
   716
	}
slouken@172
   717
	
slouken@172
   718
	element = device->firstHat;
slouken@172
   719
	i = 0;
slouken@172
   720
	while (element)
slouken@172
   721
	{
slouken@172
   722
		Uint8 pos;
slouken@172
   723
slouken@172
   724
		value = HIDGetElementValue(device, element);
slouken@172
   725
		switch(value)
slouken@172
   726
		{
slouken@172
   727
			case 0:
slouken@172
   728
				pos = SDL_HAT_CENTERED;
slouken@172
   729
				break;
slouken@172
   730
			case 1:
slouken@172
   731
				pos = SDL_HAT_UP;
slouken@172
   732
				break;
slouken@172
   733
			case 2:
slouken@172
   734
				pos = SDL_HAT_RIGHTUP;
slouken@172
   735
				break;
slouken@172
   736
			case 3:
slouken@172
   737
				pos = SDL_HAT_RIGHT;
slouken@172
   738
				break;
slouken@172
   739
			case 4:
slouken@172
   740
				pos = SDL_HAT_RIGHTDOWN;
slouken@172
   741
				break;
slouken@172
   742
			case 5:
slouken@172
   743
				pos = SDL_HAT_DOWN;
slouken@172
   744
				break;
slouken@172
   745
			case 6:
slouken@172
   746
				pos = SDL_HAT_LEFTDOWN;
slouken@172
   747
				break;
slouken@172
   748
			case 7:
slouken@172
   749
				pos = SDL_HAT_LEFT;
slouken@172
   750
				break;
slouken@172
   751
			case 8:
slouken@172
   752
				pos = SDL_HAT_LEFTUP;
slouken@172
   753
				break;
slouken@172
   754
		}
slouken@172
   755
		if ( pos != joystick->hats[i] )
slouken@172
   756
			SDL_PrivateJoystickHat(joystick, i, pos);
slouken@172
   757
		element = element->pNext;
slouken@172
   758
		++i;
slouken@172
   759
	}
slouken@172
   760
	
slouken@172
   761
	return;
slouken@172
   762
}
slouken@172
   763
slouken@172
   764
/* Function to close a joystick after use */
slouken@172
   765
void SDL_SYS_JoystickClose(SDL_Joystick *joystick)
slouken@172
   766
{
slouken@172
   767
	/* Should we do anything here? */
slouken@172
   768
	return;
slouken@172
   769
}
slouken@172
   770
slouken@172
   771
/* Function to perform any system-specific joystick related cleanup */
slouken@172
   772
void SDL_SYS_JoystickQuit(void)
slouken@172
   773
{
slouken@172
   774
	while (NULL != gpDeviceList)
slouken@172
   775
		gpDeviceList = HIDDisposeDevice (&gpDeviceList);
slouken@172
   776
}