1 /** \file joy-osx-hidmgr.c
2 * \brief Mac OS X joystick support using IOHIDManager
3 *
4 * \author Christian Vogelgsang <chris@vogelgsang.org>
5 */
6
7 /*
8 * This file is part of VICE, the Versatile Commodore Emulator.
9 * See README for copyright notice.
10 *
11 * This program is free software; you can redistribute it and/or modify
12 * it under the terms of the GNU General Public License as published by
13 * the Free Software Foundation; either version 2 of the License, or
14 * (at your option) any later version.
15 *
16 * This program is distributed in the hope that it will be useful,
17 * but WITHOUT ANY WARRANTY; without even the implied warranty of
18 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
19 * GNU General Public License for more details.
20 *
21 * You should have received a copy of the GNU General Public License
22 * along with this program; if not, write to the Free Software
23 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA
24 * 02111-1307 USA.
25 *
26 */
27
28 #define JOY_INTERNAL
29
30 #include "vice.h"
31
32 #ifdef MACOSX_SUPPORT
33
34 #include "joy.h"
35 #include "log.h"
36 #include "lib.h"
37 #include "joy-osx-hidlib.h"
38
39 #ifdef HAS_JOYSTICK
40 #ifdef HAS_HIDMGR
41
42 /* ----- Statics ----- */
43
44 static IOHIDManagerRef mgr;
45
46 /* ----- Tools ----- */
47
CFSetApplierFunctionCopyToCFArray(const void * value,void * context)48 static void CFSetApplierFunctionCopyToCFArray(const void *value, void *context)
49 {
50 CFArrayAppendValue( ( CFMutableArrayRef ) context, value );
51 }
52
IOHIDDevice_GetLongProperty(IOHIDDeviceRef inIOHIDDeviceRef,CFStringRef inKey,long * outValue)53 static Boolean IOHIDDevice_GetLongProperty( IOHIDDeviceRef inIOHIDDeviceRef, CFStringRef inKey, long * outValue )
54 {
55 Boolean result = FALSE;
56
57 if ( inIOHIDDeviceRef ) {
58 CFTypeRef tCFTypeRef = IOHIDDeviceGetProperty( inIOHIDDeviceRef, inKey );
59 if ( tCFTypeRef ) {
60 // if this is a number
61 if ( CFNumberGetTypeID() == CFGetTypeID( tCFTypeRef ) ) {
62 // get it's value
63 result = CFNumberGetValue( ( CFNumberRef ) tCFTypeRef, kCFNumberSInt32Type, outValue );
64 }
65 }
66 }
67 return result;
68 }
69
70 /* ----- API ----- */
71
joy_hidlib_init(void)72 int joy_hidlib_init(void)
73 {
74 if ( !mgr ) {
75 // create the manager
76 mgr = IOHIDManagerCreate( kCFAllocatorDefault, 0L );
77 }
78 if ( mgr ) {
79 // open it
80 IOReturn tIOReturn = IOHIDManagerOpen( mgr, 0L);
81 if ( kIOReturnSuccess != tIOReturn ) {
82 return -1;
83 } else {
84 IOHIDManagerSetDeviceMatching( mgr, NULL );
85 return 0;
86 }
87 } else {
88 return -1;
89 }
90 }
91
joy_hidlib_exit(void)92 void joy_hidlib_exit(void)
93 {
94 if(mgr) {
95 IOHIDManagerClose( mgr, 0 );
96 mgr = NULL;
97 }
98 }
99
is_joystick(IOHIDDeviceRef ref)100 static int is_joystick(IOHIDDeviceRef ref)
101 {
102 return
103 IOHIDDeviceConformsTo( ref, kHIDPage_GenericDesktop, kHIDUsage_GD_Joystick ) ||
104 IOHIDDeviceConformsTo( ref, kHIDPage_GenericDesktop, kHIDUsage_GD_GamePad );
105 }
106
count_joysticks(joy_hid_device_array_t * dev_array)107 static int count_joysticks(joy_hid_device_array_t *dev_array)
108 {
109 int i;
110 int num_devices = dev_array->num_internal_devices;
111 int num_joys = 0;
112 CFArrayRef devices = dev_array->internal_devices;
113 for ( i = 0; i < num_devices ; i++ ) {
114 IOHIDDeviceRef dev = ( IOHIDDeviceRef ) CFArrayGetValueAtIndex( devices, i );
115 if(is_joystick(dev)) {
116 num_joys++;
117 }
118 }
119
120 dev_array->num_devices = num_joys;
121 return num_joys;
122 }
123
build_joystick_list(joy_hid_device_array_t * dev_array)124 static void build_joystick_list(joy_hid_device_array_t *dev_array)
125 {
126 int i;
127 int num_devices = dev_array->num_internal_devices;
128 CFArrayRef devices = dev_array->internal_devices;
129 joy_hid_device_t *d = dev_array->devices;
130 for ( i = 0; i < num_devices ; i++ ) {
131 IOHIDDeviceRef dev = ( IOHIDDeviceRef ) CFArrayGetValueAtIndex( devices, i );
132 if(is_joystick(dev)) {
133
134 long vendor_id = 0;
135 IOHIDDevice_GetLongProperty( dev, CFSTR( kIOHIDVendorIDKey ), &vendor_id );
136 long product_id = 0;
137 IOHIDDevice_GetLongProperty( dev, CFSTR( kIOHIDProductIDKey ), &product_id );
138 CFStringRef product_key;
139 product_key = IOHIDDeviceGetProperty( dev, CFSTR( kIOHIDProductKey ) );
140 char *product_name = "N/A";
141 if(product_key) {
142 char buffer[256];
143 if(CFStringGetCString(product_key, buffer, 256, kCFStringEncodingUTF8)) {
144 product_name = strdup(buffer);
145 }
146 }
147
148 d->internal_device = dev;
149 d->vendor_id = (int)vendor_id;
150 d->product_id = (int)product_id;
151 d->serial = 0; /* will be filled in later */
152 d->product_name = product_name;
153
154 d++;
155 }
156 }
157 }
158
joy_hidlib_enumerate_devices(void)159 joy_hid_device_array_t *joy_hidlib_enumerate_devices(void)
160 {
161 if( !mgr ) {
162 return NULL;
163 }
164
165 /* create set of devices */
166 CFSetRef device_set = IOHIDManagerCopyDevices( mgr );
167 if ( !device_set ) {
168 return NULL;
169 }
170
171 /* create array of devices */
172 CFMutableArrayRef device_array = CFArrayCreateMutable( kCFAllocatorDefault, 0,
173 & kCFTypeArrayCallBacks );
174 if( ! device_array ) {
175 CFRelease( device_set );
176 return NULL;
177 }
178 CFSetApplyFunction( device_set, CFSetApplierFunctionCopyToCFArray, device_array );
179 CFRelease( device_set );
180
181 /* allocate result */
182 joy_hid_device_array_t *result =
183 (joy_hid_device_array_t *)lib_malloc(sizeof(joy_hid_device_array_t));
184 if(result == NULL) {
185 CFRelease( device_array );
186 return NULL;
187 }
188
189 /* get size */
190 CFIndex cnt = CFArrayGetCount( device_array );
191
192 /* fill internal struct */
193 result->num_internal_devices = (int)cnt;
194 result->internal_devices = device_array;
195 result->num_devices = 0;
196 result->devices = NULL;
197 result->driver_name = "IOHIDManager";
198
199 /* count joysticks -> num devices */
200 count_joysticks(result);
201 if(result->num_devices == 0)
202 return result;
203
204 /* allocate our device structs */
205 joy_hid_device_t *devices =
206 (joy_hid_device_t *)lib_malloc(sizeof(joy_hid_device_t) * result->num_devices);
207 if(devices == NULL) {
208 lib_free(result);
209 CFRelease( device_array );
210 return NULL;
211 }
212 result->devices = devices;
213
214 build_joystick_list(result);
215 return result;
216 }
217
joy_hidlib_free_devices(joy_hid_device_array_t * devices)218 void joy_hidlib_free_devices(joy_hid_device_array_t *devices)
219 {
220 if(devices == NULL) {
221 return;
222 }
223
224 int num_devices = devices->num_devices;
225 int i;
226 for(i = 0; i<num_devices; i++) {
227 joy_hidlib_free_elements(&devices->devices[i]);
228 }
229
230 if(devices->internal_devices) {
231 CFRelease( devices->internal_devices );
232 devices->internal_devices = NULL;
233 }
234
235 if(devices != NULL) {
236 lib_free(devices);
237 devices = NULL;
238 }
239 }
240
joy_hidlib_open_device(joy_hid_device_t * device)241 int joy_hidlib_open_device(joy_hid_device_t *device)
242 {
243 if( device->internal_device == NULL ) {
244 return -2;
245 }
246 IOReturn result = IOHIDDeviceOpen( device->internal_device, 0 );
247 if(result != kIOReturnSuccess) {
248 return -1;
249 } else {
250 return 0;
251 }
252 }
253
joy_hidlib_close_device(joy_hid_device_t * device)254 void joy_hidlib_close_device(joy_hid_device_t *device)
255 {
256 /* close old device */
257 if(device->internal_device != NULL) {
258 IOHIDDeviceClose(device->internal_device, 0);
259 }
260 }
261
joy_hidlib_enumerate_elements(joy_hid_device_t * device)262 int joy_hidlib_enumerate_elements(joy_hid_device_t *device)
263 {
264 IOHIDDeviceRef dev = device->internal_device;
265 if(dev == NULL) {
266 return -1;
267 }
268
269 /* get all elements of device */
270 CFArrayRef internal_elements = IOHIDDeviceCopyMatchingElements( dev, NULL, 0 );
271 if(!internal_elements) {
272 return -1;
273 }
274
275 /* get number of elements */
276 CFIndex cnt = CFArrayGetCount( internal_elements );
277 device->num_elements = (int)cnt;
278
279 /* create elements array */
280 joy_hid_element_t *elements = (joy_hid_element_t *)
281 lib_malloc(sizeof(joy_hid_element_t) * cnt);
282 if(elements == NULL) {
283 CFRelease(internal_elements);
284 internal_elements = NULL;
285 return -1;
286 }
287
288 /* enumerate and convert all elements */
289 CFIndex i;
290 joy_hid_element_t *e = elements;
291 for(i=0;i<cnt;i++) {
292 IOHIDElementRef internal_element =
293 ( IOHIDElementRef ) CFArrayGetValueAtIndex( internal_elements, i );
294 if ( internal_element ) {
295 uint32_t usage_page = IOHIDElementGetUsagePage( internal_element );
296 uint32_t usage = IOHIDElementGetUsage( internal_element );
297 CFIndex pmin = IOHIDElementGetPhysicalMin( internal_element );
298 CFIndex pmax = IOHIDElementGetPhysicalMax( internal_element );
299 CFIndex lmin = IOHIDElementGetLogicalMin( internal_element );
300 CFIndex lmax = IOHIDElementGetLogicalMax( internal_element );
301
302 e->usage_page = (int)usage_page;
303 e->usage = (int)usage;
304 e->min_pvalue = (int)pmin;
305 e->max_pvalue = (int)pmax;
306 e->min_lvalue = (int)lmin;
307 e->max_lvalue = (int)lmax;
308 e->internal_element = internal_element;
309 } else {
310 e->usage_page = -1;
311 e->usage = -1;
312 e->min_pvalue = -1;
313 e->max_pvalue = -1;
314 e->min_lvalue = -1;
315 e->max_lvalue = -1;
316 e->internal_element = NULL;
317 }
318 e++;
319 }
320
321 /* keep the reference until the elements are free'ed again */
322 device->internal_elements = internal_elements;
323 device->elements = elements;
324
325 return (int)cnt;
326 }
327
joy_hidlib_free_elements(joy_hid_device_t * device)328 void joy_hidlib_free_elements(joy_hid_device_t *device)
329 {
330 if(device == NULL) {
331 return;
332 }
333 if(device->elements) {
334 lib_free(device->elements);
335 device->elements = NULL;
336 }
337 if(device->internal_elements) {
338 CFRelease(device->internal_elements);
339 device->internal_elements = NULL;
340 }
341 }
342
joy_hidlib_get_value(joy_hid_device_t * device,joy_hid_element_t * element,int * value,int phys)343 int joy_hidlib_get_value(joy_hid_device_t *device,
344 joy_hid_element_t *element,
345 int *value, int phys)
346 {
347 IOHIDValueRef value_ref;
348 IOReturn result = IOHIDDeviceGetValue( device->internal_device,
349 element->internal_element,
350 &value_ref );
351 if(result == kIOReturnSuccess) {
352 if(phys) {
353 *value = (int)IOHIDValueGetScaledValue( value_ref, kIOHIDValueScaleTypePhysical );
354 } else {
355 *value = (int)IOHIDValueGetIntegerValue( value_ref );
356 }
357 return 0;
358 } else {
359 return -1;
360 }
361 }
362
363 #endif /* HAS_HIDMGR */
364 #endif /* HAS_JOYSTICK */
365 #endif
366
367