Skip to content

Commit 0828211

Browse files
pedro-wSiegeLord
authored andcommitted
Developed by @NewCreature to make enumerating connected joysticks more robust. The current implementation assumes each HID device is one controller/joystick. The patch fixes this by interpreting each collection reported by the device as a separate controller/joystick (this is part of the USB HID spec). The names of the various elements are generated from the data reported by the HID device.
1 parent cdbc0a9 commit 0828211

File tree

1 file changed

+183
-39
lines changed

1 file changed

+183
-39
lines changed

src/macosx/hidjoy.m

+183-39
Original file line numberDiff line numberDiff line change
@@ -50,8 +50,11 @@
5050
#define JOYSTICK_USAGE_NUMBER 0x04
5151
#define GAMEPAD_USAGE_NUMBER 0x05
5252

53+
#define JOYSTICK_NAME_MAX 256
54+
5355
typedef struct {
5456
ALLEGRO_JOYSTICK parent;
57+
char name[JOYSTICK_NAME_MAX];
5558
IOHIDElementRef buttons[_AL_MAX_JOYSTICK_BUTTONS];
5659
IOHIDElementRef axes[_AL_MAX_JOYSTICK_STICKS][_AL_MAX_JOYSTICK_AXES];
5760
IOHIDElementRef dpad;
@@ -130,12 +133,37 @@ static CFMutableDictionaryRef CreateDeviceMatchingDictionary(
130133
return result;
131134
}
132135

133-
static ALLEGRO_JOYSTICK_OSX *find_joystick(IOHIDDeviceRef ident)
136+
static bool joystick_uses_element(ALLEGRO_JOYSTICK_OSX *joy, IOHIDElementRef elem)
137+
{
138+
int i, j;
139+
140+
if(elem) {
141+
for (i = 0; i < joy->parent.info.num_buttons; i++) {
142+
if(joy->buttons[i] == elem)
143+
return true;
144+
}
145+
for (i = 0; i < joy->parent.info.num_sticks; i++) {
146+
for(j = 0; j < joy->parent.info.stick[i].num_axes; j++) {
147+
if(joy->axes[i][j] == elem)
148+
return true;
149+
}
150+
}
151+
if (joy->dpad == elem)
152+
return true;
153+
}
154+
else {
155+
return true;
156+
}
157+
158+
return false;
159+
}
160+
161+
static ALLEGRO_JOYSTICK_OSX *find_joystick(IOHIDDeviceRef ident, IOHIDElementRef elem)
134162
{
135163
int i;
136164
for (i = 0; i < (int)_al_vector_size(&joysticks); i++) {
137165
ALLEGRO_JOYSTICK_OSX *joy = *(ALLEGRO_JOYSTICK_OSX **)_al_vector_ref(&joysticks, i);
138-
if (ident == joy->ident) {
166+
if (ident == joy->ident && joystick_uses_element(joy, elem)) {
139167
return joy;
140168
}
141169
}
@@ -145,10 +173,12 @@ static CFMutableDictionaryRef CreateDeviceMatchingDictionary(
145173

146174
static const char *get_element_name(IOHIDElementRef elem, const char *default_name)
147175
{
176+
const char *name_cstr = NULL;
148177
CFStringRef name = IOHIDElementGetName(elem);
149-
if (name) {
150-
return CFStringGetCStringPtr(name, kCFStringEncodingUTF8);
151-
}
178+
if (name)
179+
name_cstr = CFStringGetCStringPtr(name, kCFStringEncodingUTF8);
180+
if (name_cstr)
181+
return name_cstr;
152182
else
153183
return default_name;
154184
}
@@ -182,31 +212,58 @@ static void add_axis(ALLEGRO_JOYSTICK_OSX *joy, int stick_index, int axis_index,
182212
joy->axes[stick_index][axis_index] = elem;
183213
}
184214

185-
static void add_elements(CFArrayRef elements, ALLEGRO_JOYSTICK_OSX *joy)
215+
static void add_elements(CFArrayRef elements, ALLEGRO_JOYSTICK_OSX *joy, int device_joystick)
186216
{
187-
int i;
217+
int i, start_i = 0;
188218
char default_name[100];
189219
int stick_class = -1;
190220
int axis_index = 0;
221+
bool collection_started = false;
222+
int current_joystick = -1;
191223

192224
joy_null(joy);
193225

226+
/* look for device_joystick */
194227
for (i = 0; i < CFArrayGetCount(elements); i++) {
195228
IOHIDElementRef elem = (IOHIDElementRef)CFArrayGetValueAtIndex(
196229
elements,
197230
i
198231
);
232+
int etype = IOHIDElementGetType(elem);
233+
if (etype == kIOHIDElementTypeCollection) {
234+
collection_started = true;
235+
}
236+
else if (etype == kIOHIDElementTypeInput_Button || etype == kIOHIDElementTypeInput_Misc) {
237+
if (collection_started) {
238+
current_joystick++;
239+
collection_started = false;
240+
if (current_joystick == device_joystick) {
241+
start_i = i;
242+
break;
243+
}
244+
}
245+
}
246+
}
247+
248+
for (i = start_i; i < CFArrayGetCount(elements); i++) {
249+
IOHIDElementRef elem = (IOHIDElementRef)CFArrayGetValueAtIndex(
250+
elements,
251+
i
252+
);
199253

200254
int usage = IOHIDElementGetUsage(elem);
255+
if (IOHIDElementGetType(elem) == kIOHIDElementTypeCollection) {
256+
break;
257+
}
201258
if (IOHIDElementGetType(elem) == kIOHIDElementTypeInput_Button) {
202-
if (usage >= 0 && usage < _AL_MAX_JOYSTICK_BUTTONS &&
203-
!joy->buttons[usage-1]) {
204-
joy->buttons[usage-1] = elem;
205-
sprintf(default_name, "Button %d", usage-1);
259+
if (usage >= 0 && joy->parent.info.num_buttons < _AL_MAX_JOYSTICK_BUTTONS &&
260+
!joy->buttons[joy->parent.info.num_buttons]) {
261+
joy->buttons[joy->parent.info.num_buttons] = elem;
262+
sprintf(default_name, "Button %d", joy->parent.info.num_buttons);
206263
const char *name = get_element_name(elem, default_name);
207264
char *str = al_malloc(strlen(name)+1);
208265
strcpy(str, name);
209-
joy->parent.info.button[usage-1].name = str;
266+
joy->parent.info.button[joy->parent.info.num_buttons].name = str;
210267
joy->parent.info.num_buttons++;
211268
}
212269
}
@@ -216,18 +273,33 @@ static void add_elements(CFArrayRef elements, ALLEGRO_JOYSTICK_OSX *joy)
216273
long max = IOHIDElementGetLogicalMax(elem);
217274
int new_stick_class = -1;
218275
int stick_index = joy->parent.info.num_sticks - 1;
276+
int axis_type = -1;
219277

220278
switch (usage) {
221279
case kHIDUsage_GD_X:
280+
new_stick_class = 1;
281+
axis_type = 0;
282+
break;
222283
case kHIDUsage_GD_Y:
284+
new_stick_class = 1;
285+
axis_type = 1;
286+
break;
223287
case kHIDUsage_GD_Z:
224288
new_stick_class = 1;
289+
axis_type = 2;
225290
break;
226291

227292
case kHIDUsage_GD_Rx:
293+
new_stick_class = 2;
294+
axis_type = 0;
295+
break;
228296
case kHIDUsage_GD_Ry:
297+
new_stick_class = 2;
298+
axis_type = 1;
299+
break;
229300
case kHIDUsage_GD_Rz:
230301
new_stick_class = 2;
302+
axis_type = 2;
231303
break;
232304

233305
case kHIDUsage_GD_Hatswitch:
@@ -250,7 +322,19 @@ static void add_elements(CFArrayRef elements, ALLEGRO_JOYSTICK_OSX *joy)
250322
stick_class = new_stick_class;
251323

252324
char *buf = al_malloc(20);
253-
sprintf(buf, "Stick %d", stick_index);
325+
switch (stick_class) {
326+
case 1:
327+
sprintf(buf, "Primary Stick");
328+
break;
329+
case 2:
330+
sprintf(buf, "Secondary Stick");
331+
break;
332+
case 3:
333+
sprintf(buf, "Hat Switch");
334+
break;
335+
default:
336+
sprintf(buf, "Stick %d", stick_index);
337+
}
254338
joy->parent.info.stick[stick_index].name = buf;
255339
}
256340
else
@@ -261,14 +345,14 @@ static void add_elements(CFArrayRef elements, ALLEGRO_JOYSTICK_OSX *joy)
261345
joy->dpad = elem;
262346

263347
joy->dpad_axis_horiz = axis_index;
264-
sprintf(default_name, "Axis %i", axis_index);
348+
sprintf(default_name, "X-Axis");
265349
char *str = al_malloc(strlen(default_name)+1);
266350
strcpy(str, default_name);
267351
joy->parent.info.stick[stick_index].axis[axis_index].name = str;
268352

269353
++axis_index;
270354
joy->dpad_axis_vert = axis_index;
271-
sprintf(default_name, "Axis %i", axis_index);
355+
sprintf(default_name, "Y-Axis");
272356
str = al_malloc(strlen(default_name)+1);
273357
strcpy(str, default_name);
274358
add_axis(joy, stick_index, axis_index, min, max, str, elem);
@@ -277,7 +361,19 @@ static void add_elements(CFArrayRef elements, ALLEGRO_JOYSTICK_OSX *joy)
277361
joy->parent.info.stick[stick_index].num_axes = 2;
278362
}
279363
else {
280-
sprintf(default_name, "Axis %i", axis_index);
364+
switch (axis_type) {
365+
case 0:
366+
sprintf(default_name, "X-Axis");
367+
break;
368+
case 1:
369+
sprintf(default_name, "Y-Axis");
370+
break;
371+
case 2:
372+
sprintf(default_name, "Z-Axis");
373+
break;
374+
default:
375+
sprintf(default_name, "Axis %i", axis_index);
376+
}
281377
const char *name = get_element_name(elem, default_name);
282378
char *str = al_malloc(strlen(name)+1);
283379
strcpy(str, name);
@@ -298,44 +394,92 @@ static void osx_joy_generate_configure_event(void)
298394
_al_generate_joystick_event(&event);
299395
}
300396

397+
static int device_count_joysticks(IOHIDDeviceRef ref)
398+
{
399+
int i;
400+
int count = 0;
401+
bool collection_started = false;
402+
403+
CFArrayRef elements = IOHIDDeviceCopyMatchingElements(
404+
ref,
405+
NULL,
406+
kIOHIDOptionsTypeNone
407+
);
408+
for (i = 0; i < CFArrayGetCount(elements); i++) {
409+
IOHIDElementRef elem = (IOHIDElementRef)CFArrayGetValueAtIndex(
410+
elements,
411+
i
412+
);
413+
414+
int etype = IOHIDElementGetType(elem);
415+
if (etype == kIOHIDElementTypeCollection) {
416+
collection_started = true;
417+
}
418+
else if (etype == kIOHIDElementTypeInput_Button || etype == kIOHIDElementTypeInput_Misc) {
419+
if (collection_started) {
420+
count++;
421+
collection_started = false;
422+
}
423+
}
424+
}
425+
CFRelease(elements);
426+
return count;
427+
}
428+
429+
static void device_setup_joystick(IOHIDDeviceRef ref, ALLEGRO_JOYSTICK_OSX *joy, int device_joystick)
430+
{
431+
CFStringRef product_name;
432+
joy->cfg_state = new_joystick_state;
433+
434+
CFArrayRef elements = IOHIDDeviceCopyMatchingElements(
435+
ref,
436+
NULL,
437+
kIOHIDOptionsTypeNone
438+
);
439+
440+
add_elements(elements, joy, device_joystick);
441+
product_name = IOHIDDeviceGetProperty(ref, CFSTR(kIOHIDProductKey));
442+
if(product_name)
443+
{
444+
CFStringGetCString(product_name, joy->name, JOYSTICK_NAME_MAX, kCFStringEncodingUTF8);
445+
}
446+
CFRelease(elements);
447+
}
448+
301449
static void device_add_callback(
302450
void *context,
303451
IOReturn result,
304452
void *sender,
305453
IOHIDDeviceRef ref
306454
) {
455+
int i;
456+
int device_joysticks;
307457
(void)context;
308458
(void)result;
309459
(void)sender;
310460

311461
al_lock_mutex(add_mutex);
312462

313-
ALLEGRO_JOYSTICK_OSX *joy = find_joystick(ref);
463+
ALLEGRO_JOYSTICK_OSX *joy = find_joystick(ref, NULL);
314464
if (joy == NULL) {
315-
joy = al_calloc(1, sizeof(ALLEGRO_JOYSTICK_OSX));
316-
joy->ident = ref;
317-
ALLEGRO_JOYSTICK_OSX **back = _al_vector_alloc_back(&joysticks);
318-
*back = joy;
465+
device_joysticks = device_count_joysticks(ref);
466+
for (i = 0; i < device_joysticks; i++) {
467+
joy = al_calloc(1, sizeof(ALLEGRO_JOYSTICK_OSX));
468+
joy->ident = ref;
469+
ALLEGRO_JOYSTICK_OSX **back = _al_vector_alloc_back(&joysticks);
470+
*back = joy;
471+
device_setup_joystick(ref, joy, i);
472+
ALLEGRO_INFO("Found joystick (%d buttons, %d sticks)\n",
473+
joy->parent.info.num_buttons, joy->parent.info.num_sticks);
474+
}
475+
}
476+
else {
477+
device_setup_joystick(ref, joy, 0);
319478
}
320-
joy->cfg_state = new_joystick_state;
321-
322-
CFArrayRef elements = IOHIDDeviceCopyMatchingElements(
323-
ref,
324-
NULL,
325-
kIOHIDOptionsTypeNone
326-
);
327-
328-
add_elements(elements, joy);
329-
330-
CFRelease(elements);
331-
332479

333480
al_unlock_mutex(add_mutex);
334481

335482
osx_joy_generate_configure_event();
336-
337-
ALLEGRO_INFO("Found joystick (%d buttons, %d sticks)\n",
338-
joy->parent.info.num_buttons, joy->parent.info.num_sticks);
339483
}
340484

341485
static void device_remove_callback(
@@ -432,7 +576,7 @@ static void value_callback(
432576

433577
IOHIDElementRef elem = IOHIDValueGetElement(value);
434578
IOHIDDeviceRef ref = IOHIDElementGetDevice(elem);
435-
ALLEGRO_JOYSTICK_OSX *joy = find_joystick(ref);
579+
ALLEGRO_JOYSTICK_OSX *joy = find_joystick(ref, elem);
436580

437581
if (!joy) return;
438582

@@ -733,8 +877,8 @@ static bool reconfigure_joysticks(void)
733877
// FIXME!
734878
static const char *get_joystick_name(ALLEGRO_JOYSTICK *joy_)
735879
{
736-
(void)joy_;
737-
return "Joystick";
880+
ALLEGRO_JOYSTICK_OSX *joy = (ALLEGRO_JOYSTICK_OSX *)joy_;
881+
return joy->name;
738882
}
739883

740884
static bool get_joystick_active(ALLEGRO_JOYSTICK *joy_)

0 commit comments

Comments
 (0)