1 /** \file joy-osx.c
2 * \brief Mac OS X joystick support
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 "cmdline.h"
35 #include "joy.h"
36 #include "joyport.h"
37 #include "joystick.h"
38 #include "keyboard.h"
39 #include "lib.h"
40 #include "log.h"
41 #include "resources.h"
42 #include "types.h"
43 #include "util.h"
44
45 #ifdef HAS_JOYSTICK
46
47 /* ----- Static Data ------------------------------------------------------ */
48
49 static int joy_done_init = 0;
50
51 /* number of joyports and extra joyports */
52 int joy_num_ports;
53 int joy_num_extra_ports;
54
55 joy_hid_descriptor_t hid_a;
56 joy_hid_descriptor_t hid_b;
57
58 /* the driver holds up to two USB joystick definitions */
59 joystick_descriptor_t joy_a = { .hid = &hid_a };
60 joystick_descriptor_t joy_b = { .hid = &hid_b };
61
62 /* ----- VICE Resources --------------------------------------------------- */
63
64 static void setup_axis_mapping(joystick_descriptor_t *joy);
65 static void setup_button_mapping(joystick_descriptor_t *joy);
66 static void setup_auto_button_mapping(joystick_descriptor_t *joy);
67 static void setup_hat_switch_mapping(joystick_descriptor_t *joy);
68 static void setup_auto(void);
69
70 /* FIXME: implement listing the joystick devices here */
71
72 /** \brief Struct containing device name and id
73 */
74 typedef struct device_info_s {
75 const char *name; /**< device name */
76 int id; /**< device ID (\see joy.h) */
77 } device_info_t;
78
79 static device_info_t predefined_device_list[] = {
80 #ifdef HAS_JOYSTICK
81 { "Analog joystick 0", JOYDEV_ANALOG_0 },
82 { "Analog joystick 1", JOYDEV_ANALOG_1 },
83 { "Analog joystick 2", JOYDEV_ANALOG_2 },
84 { "Analog joystick 3", JOYDEV_ANALOG_3 },
85 { "Analog joystick 4", JOYDEV_ANALOG_4 },
86 { "Analog joystick 5", JOYDEV_ANALOG_5 },
87 #endif
88 #ifdef HAS_DIGITAL_JOYSTICK
89 { "Digital joystick 0", JOYDEV_DIGITAL_0 },
90 { "Digital joystick 1", JOYDEV_DIGITAL_1 },
91 #endif
92 #ifdef HAS_USB_JOYSTICK
93 { "USB joystick 0", JOYDEV_USB_0 },
94 { "USB joystick 1", JOYDEV_USB_1 },
95 #endif
96 { NULL, -1 }
97 };
98
99 static int joystickdeviceidx = 0;
100
joystick_ui_reset_device_list(void)101 void joystick_ui_reset_device_list(void)
102 {
103 joystickdeviceidx = 0;
104 }
105
joystick_ui_get_next_device_name(int * id)106 const char *joystick_ui_get_next_device_name(int *id)
107 {
108 const char *name;
109 if ((name = predefined_device_list[joystickdeviceidx].name)) {
110 *id = predefined_device_list[joystickdeviceidx].id;
111 joystickdeviceidx++;
112 return name;
113 }
114 return NULL;
115 }
116
117 /* HID settings */
118
set_joy_a_device_name(const char * val,void * param)119 static int set_joy_a_device_name(const char *val,void *param)
120 {
121 util_string_set(&joy_a.device_name, val);
122 if (joy_done_init) {
123 setup_auto();
124 }
125 return 0;
126 }
127
set_joy_a_x_axis_name(const char * val,void * param)128 static int set_joy_a_x_axis_name(const char *val,void *param)
129 {
130 util_string_set(&joy_a.axis[HID_X_AXIS].name, val);
131 if (joy_done_init) {
132 setup_axis_mapping(&joy_a);
133 }
134 return 0;
135 }
136
set_joy_a_y_axis_name(const char * val,void * param)137 static int set_joy_a_y_axis_name(const char *val,void *param)
138 {
139 util_string_set(&joy_a.axis[HID_Y_AXIS].name, val);
140 if (joy_done_init) {
141 setup_axis_mapping(&joy_a);
142 }
143 return 0;
144 }
145
set_joy_a_button_mapping(const char * val,void * param)146 static int set_joy_a_button_mapping(const char *val,void *param)
147 {
148 util_string_set(&joy_a.button_mapping, val);
149 if (joy_done_init) {
150 setup_button_mapping(&joy_a);
151 }
152 return 0;
153 }
154
set_joy_a_auto_button_mapping(const char * val,void * param)155 static int set_joy_a_auto_button_mapping(const char *val,void *param)
156 {
157 util_string_set(&joy_a.auto_button_mapping, val);
158 if (joy_done_init) {
159 setup_auto_button_mapping(&joy_a);
160 }
161 return 0;
162 }
163
164 /* a threshold */
165
set_joy_a_x_threshold(int val,void * param)166 static int set_joy_a_x_threshold(int val, void *param)
167 {
168 if (val < 0 || val > 100) {
169 return -1;
170 }
171
172 if (joy_a.axis[HID_X_AXIS].threshold != val) {
173 joy_a.axis[HID_X_AXIS].threshold = val;
174 if (joy_done_init) {
175 setup_axis_mapping(&joy_a);
176 }
177 }
178 return 0;
179 }
180
set_joy_a_y_threshold(int val,void * param)181 static int set_joy_a_y_threshold(int val, void *param)
182 {
183 if (val < 0 || val > 100) {
184 return -1;
185 }
186
187 if (joy_a.axis[HID_Y_AXIS].threshold != val) {
188 joy_a.axis[HID_Y_AXIS].threshold = val;
189 if (joy_done_init) {
190 setup_axis_mapping(&joy_a);
191 }
192 }
193 return 0;
194 }
195
196 /* min */
197
set_joy_a_x_min(int val,void * param)198 static int set_joy_a_x_min(int val, void *param)
199 {
200 if (joy_a.axis[HID_X_AXIS].min != val) {
201 joy_a.axis[HID_X_AXIS].min = val;
202 if (joy_done_init) {
203 setup_axis_mapping(&joy_a);
204 }
205 }
206 return 0;
207 }
208
set_joy_a_y_min(int val,void * param)209 static int set_joy_a_y_min(int val, void *param)
210 {
211 if (joy_a.axis[HID_Y_AXIS].min != val) {
212 joy_a.axis[HID_Y_AXIS].min = val;
213 if (joy_done_init) {
214 setup_axis_mapping(&joy_a);
215 }
216 }
217 return 0;
218 }
219
220 /* max */
221
set_joy_a_x_max(int val,void * param)222 static int set_joy_a_x_max(int val, void *param)
223 {
224 if (joy_a.axis[HID_X_AXIS].max != val) {
225 joy_a.axis[HID_X_AXIS].max = val;
226 if (joy_done_init) {
227 setup_axis_mapping(&joy_a);
228 }
229 }
230 return 0;
231 }
232
set_joy_a_y_max(int val,void * param)233 static int set_joy_a_y_max(int val, void *param)
234 {
235 if (joy_a.axis[HID_Y_AXIS].max != val) {
236 joy_a.axis[HID_Y_AXIS].max = val;
237 if (joy_done_init) {
238 setup_axis_mapping(&joy_a);
239 }
240 }
241 return 0;
242 }
243
244 /* logical */
245
set_joy_a_x_logical(int val,void * param)246 static int set_joy_a_x_logical(int val, void *param)
247 {
248 if (joy_a.axis[HID_X_AXIS].logical != val) {
249 joy_a.axis[HID_X_AXIS].logical = val;
250 if (joy_done_init) {
251 setup_axis_mapping(&joy_a);
252 }
253 }
254 return 0;
255 }
256
set_joy_a_y_logical(int val,void * param)257 static int set_joy_a_y_logical(int val, void *param)
258 {
259 if (joy_a.axis[HID_Y_AXIS].logical != val) {
260 joy_a.axis[HID_Y_AXIS].logical = val;
261 if (joy_done_init) {
262 setup_axis_mapping(&joy_a);
263 }
264 }
265 return 0;
266 }
267
set_joy_b_device_name(const char * val,void * param)268 static int set_joy_b_device_name(const char *val,void *param)
269 {
270 util_string_set(&joy_b.device_name, val);
271 if (joy_done_init) {
272 setup_auto();
273 }
274 return 0;
275 }
276
set_joy_b_x_axis_name(const char * val,void * param)277 static int set_joy_b_x_axis_name(const char *val,void *param)
278 {
279 util_string_set(&joy_b.axis[HID_X_AXIS].name, val);
280 if (joy_done_init) {
281 setup_axis_mapping(&joy_b);
282 }
283 return 0;
284 }
285
set_joy_b_y_axis_name(const char * val,void * param)286 static int set_joy_b_y_axis_name(const char *val,void *param)
287 {
288 util_string_set(&joy_b.axis[HID_Y_AXIS].name, val);
289 if (joy_done_init) {
290 setup_axis_mapping(&joy_b);
291 }
292 return 0;
293 }
294
set_joy_b_button_mapping(const char * val,void * param)295 static int set_joy_b_button_mapping(const char *val,void *param)
296 {
297 util_string_set(&joy_b.button_mapping, val);
298 if (joy_done_init) {
299 setup_button_mapping(&joy_b);
300 }
301 return 0;
302 }
303
set_joy_b_auto_button_mapping(const char * val,void * param)304 static int set_joy_b_auto_button_mapping(const char *val,void *param)
305 {
306 util_string_set(&joy_b.auto_button_mapping, val);
307 if (joy_done_init) {
308 setup_auto_button_mapping(&joy_b);
309 }
310 return 0;
311 }
312
313 /* threshold */
314
set_joy_b_x_threshold(int val,void * param)315 static int set_joy_b_x_threshold(int val, void *param)
316 {
317 if (val < 0 || val > 100) {
318 return -1;
319 }
320
321 if (joy_b.axis[HID_X_AXIS].threshold != val) {
322 joy_b.axis[HID_X_AXIS].threshold = val;
323 if (joy_done_init) {
324 setup_axis_mapping(&joy_b);
325 }
326 }
327 return 0;
328 }
329
set_joy_b_y_threshold(int val,void * param)330 static int set_joy_b_y_threshold(int val, void *param)
331 {
332 if (val < 0 || val > 100) {
333 return -1;
334 }
335
336 if (joy_b.axis[HID_Y_AXIS].threshold != val) {
337 joy_b.axis[HID_Y_AXIS].threshold = val;
338 if (joy_done_init) {
339 setup_axis_mapping(&joy_b);
340 }
341 }
342 return 0;
343 }
344
345 /* min */
346
set_joy_b_x_min(int val,void * param)347 static int set_joy_b_x_min(int val, void *param)
348 {
349 if (joy_b.axis[HID_X_AXIS].min != val) {
350 joy_b.axis[HID_X_AXIS].min = val;
351 if (joy_done_init) {
352 setup_axis_mapping(&joy_b);
353 }
354 }
355 return 0;
356 }
357
set_joy_b_y_min(int val,void * param)358 static int set_joy_b_y_min(int val, void *param)
359 {
360 if (joy_b.axis[HID_Y_AXIS].min != val) {
361 joy_b.axis[HID_Y_AXIS].min = val;
362 if (joy_done_init) {
363 setup_axis_mapping(&joy_b);
364 }
365 }
366 return 0;
367 }
368
369 /* max */
370
set_joy_b_x_max(int val,void * param)371 static int set_joy_b_x_max(int val, void *param)
372 {
373 if (joy_b.axis[HID_X_AXIS].max != val) {
374 joy_b.axis[HID_X_AXIS].max = val;
375 if (joy_done_init) {
376 setup_axis_mapping(&joy_b);
377 }
378 }
379 return 0;
380 }
381
set_joy_b_y_max(int val,void * param)382 static int set_joy_b_y_max(int val, void *param)
383 {
384 if (joy_b.axis[HID_Y_AXIS].max != val) {
385 joy_b.axis[HID_Y_AXIS].max = val;
386 if (joy_done_init) {
387 setup_axis_mapping(&joy_b);
388 }
389 }
390 return 0;
391 }
392
393 /* logical */
394
set_joy_b_x_logical(int val,void * param)395 static int set_joy_b_x_logical(int val, void *param)
396 {
397 if (joy_b.axis[HID_X_AXIS].logical != val) {
398 joy_b.axis[HID_X_AXIS].logical = val;
399 if (joy_done_init) {
400 setup_axis_mapping(&joy_b);
401 }
402 }
403 return 0;
404 }
405
set_joy_b_y_logical(int val,void * param)406 static int set_joy_b_y_logical(int val, void *param)
407 {
408 if (joy_b.axis[HID_Y_AXIS].logical != val) {
409 joy_b.axis[HID_Y_AXIS].logical = val;
410 if (joy_done_init) {
411 setup_axis_mapping(&joy_b);
412 }
413 }
414 return 0;
415 }
416
set_joy_a_hat_switch(int val,void * param)417 static int set_joy_a_hat_switch(int val, void *param)
418 {
419 if (val != joy_a.hat_switch.id) {
420 joy_a.hat_switch.id = val;
421 if (joy_done_init) {
422 setup_hat_switch_mapping(&joy_a);
423 }
424 }
425 return 0;
426 }
427
set_joy_b_hat_switch(int val,void * param)428 static int set_joy_b_hat_switch(int val, void *param)
429 {
430 if (val != joy_b.hat_switch.id) {
431 joy_b.hat_switch.id = val;
432 if (joy_done_init) {
433 setup_hat_switch_mapping(&joy_b);
434 }
435 }
436 return 0;
437 }
438
439 static const resource_string_t resources_string[] = {
440 { "JoyADevice", "", RES_EVENT_NO, NULL,
441 &joy_a.device_name, set_joy_a_device_name, NULL },
442 { "JoyAXAxis", "X", RES_EVENT_NO, NULL,
443 &joy_a.axis[HID_X_AXIS].name, set_joy_a_x_axis_name, NULL },
444 { "JoyAYAxis", "Y", RES_EVENT_NO, NULL,
445 &joy_a.axis[HID_Y_AXIS].name, set_joy_a_y_axis_name, NULL },
446 { "JoyAButtons", "1:2:0:0:0:0", RES_EVENT_NO, NULL,
447 &joy_a.button_mapping, set_joy_a_button_mapping, NULL },
448 { "JoyAAutoButtons", "3:4:2:2:4:4", RES_EVENT_NO, NULL,
449 &joy_a.auto_button_mapping, set_joy_a_auto_button_mapping, NULL },
450 { "JoyBDevice", "", RES_EVENT_NO, NULL,
451 &joy_b.device_name, set_joy_b_device_name, NULL },
452 { "JoyBXAxis", "X", RES_EVENT_NO, NULL,
453 &joy_b.axis[HID_X_AXIS].name, set_joy_b_x_axis_name, NULL },
454 { "JoyBYAxis", "Y", RES_EVENT_NO, NULL,
455 &joy_b.axis[HID_Y_AXIS].name, set_joy_b_y_axis_name, NULL },
456 { "JoyBButtons", "1:2:0:0:0:0", RES_EVENT_NO, NULL,
457 &joy_b.button_mapping, set_joy_b_button_mapping, NULL },
458 { "JoyBAutoButtons", "3:4:2:2:4:4", RES_EVENT_NO, NULL,
459 &joy_b.auto_button_mapping, set_joy_b_auto_button_mapping, NULL },
460 RESOURCE_STRING_LIST_END
461 };
462
463 static const resource_int_t resources_int[] = {
464 /* Axis ... Threshold */
465 { "JoyAXThreshold", 50, RES_EVENT_NO, NULL,
466 &joy_a.axis[HID_X_AXIS].threshold, set_joy_a_x_threshold, NULL },
467 { "JoyAYThreshold", 50, RES_EVENT_NO, NULL,
468 &joy_a.axis[HID_Y_AXIS].threshold, set_joy_a_y_threshold, NULL },
469 { "JoyBXThreshold", 50, RES_EVENT_NO, NULL,
470 &joy_b.axis[HID_X_AXIS].threshold, set_joy_b_x_threshold, NULL },
471 { "JoyBYThreshold", 50, RES_EVENT_NO, NULL,
472 &joy_b.axis[HID_Y_AXIS].threshold, set_joy_b_y_threshold, NULL },
473
474 /* Axis ... Min */
475 { "JoyAXMin", 0, RES_EVENT_NO, NULL,
476 &joy_a.axis[HID_X_AXIS].min, set_joy_a_x_min, NULL },
477 { "JoyAYMin", 0, RES_EVENT_NO, NULL,
478 &joy_a.axis[HID_Y_AXIS].min, set_joy_a_y_min, NULL },
479 { "JoyBXMin", 0, RES_EVENT_NO, NULL,
480 &joy_b.axis[HID_X_AXIS].min, set_joy_b_x_min, NULL },
481 { "JoyBYMin", 0, RES_EVENT_NO, NULL,
482 &joy_b.axis[HID_Y_AXIS].min, set_joy_b_y_min, NULL },
483
484 /* Axis ... Max */
485 { "JoyAXMax", 0, RES_EVENT_NO, NULL,
486 &joy_a.axis[HID_X_AXIS].max, set_joy_a_x_max, NULL },
487 { "JoyAYMax", 0, RES_EVENT_NO, NULL,
488 &joy_a.axis[HID_Y_AXIS].max, set_joy_a_y_max, NULL },
489 { "JoyBXMax", 0, RES_EVENT_NO, NULL,
490 &joy_b.axis[HID_X_AXIS].max, set_joy_b_x_max, NULL },
491 { "JoyBYMax", 0, RES_EVENT_NO, NULL,
492 &joy_b.axis[HID_Y_AXIS].max, set_joy_b_y_max, NULL },
493
494 /* Axis ... Logical */
495 { "JoyAXLogical", 0, RES_EVENT_NO, NULL,
496 &joy_a.axis[HID_X_AXIS].logical, set_joy_a_x_logical, NULL },
497 { "JoyAYLogical", 0, RES_EVENT_NO, NULL,
498 &joy_a.axis[HID_Y_AXIS].logical, set_joy_a_y_logical, NULL },
499 { "JoyBXLogical", 0, RES_EVENT_NO, NULL,
500 &joy_b.axis[HID_X_AXIS].logical, set_joy_b_x_logical, NULL },
501 { "JoyBYLogical", 0, RES_EVENT_NO, NULL,
502 &joy_b.axis[HID_Y_AXIS].logical, set_joy_b_y_logical, NULL },
503
504 { "JoyAHatSwitch", 1, RES_EVENT_NO, NULL,
505 &joy_a.hat_switch.id, set_joy_a_hat_switch, NULL },
506 { "JoyBHatSwitch", 1, RES_EVENT_NO, NULL,
507 &joy_b.hat_switch.id, set_joy_b_hat_switch, NULL },
508 RESOURCE_INT_LIST_END
509 };
510
joy_arch_resources_init(void)511 int joy_arch_resources_init(void)
512 {
513 if (resources_register_string(resources_string) < 0) {
514 return -1;
515 }
516
517 return resources_register_int(resources_int);
518 }
519
520 /* ----- VICE Command-line options ----- */
521
522 static const cmdline_option_t cmdline_options[] =
523 {
524 { "-joyAdevice", SET_RESOURCE, CMDLINE_ATTRIB_NEED_ARGS,
525 NULL, NULL, "JoyADevice", NULL,
526 "<vid:pid:sn>", "Set HID A device" },
527 { "-joyAxaxis", SET_RESOURCE, CMDLINE_ATTRIB_NEED_ARGS,
528 NULL, NULL, "JoyAXAxis", NULL,
529 "<X,Y,Z,Rx,Ry,Rz>", "Set X Axis for HID A device" },
530 { "-joyAyaxis", SET_RESOURCE, CMDLINE_ATTRIB_NEED_ARGS,
531 NULL, NULL, "JoyAYAxis", NULL,
532 "<X,Y,Z,Rx,Ry,Rz>", "Set Y Axis for HID A device" },
533 { "-joyAbuttons", SET_RESOURCE, CMDLINE_ATTRIB_NEED_ARGS,
534 NULL, NULL, "JoyAButtons", NULL,
535 "<f:af:l:r:u:d>", "Set Buttons for HID A device" },
536 { "-joyAautobuttons", SET_RESOURCE, CMDLINE_ATTRIB_NEED_ARGS,
537 NULL, NULL, "JoyAAutoButtons", NULL,
538 "<af1:af2:af1p:af1r:af2p:af2r>", "Set Auto Fire Buttons for HID A device" },
539 { "-joyAxthreshold", SET_RESOURCE, CMDLINE_ATTRIB_NEED_ARGS,
540 NULL, NULL, "JoyAXThreshold", NULL,
541 "<0-100>", "Set X Axis Threshold in Percent of HID A device" },
542 { "-joyAythreshold", SET_RESOURCE, CMDLINE_ATTRIB_NEED_ARGS,
543 NULL, NULL, "JoyAYThreshold", NULL,
544 "<0-100>", "Set Y Axis Threshold in Percent of HID A device" },
545 { "-joyBdevice", SET_RESOURCE, CMDLINE_ATTRIB_NEED_ARGS,
546 NULL, NULL, "JoyBDevice", NULL,
547 "<vid:pid:sn>", "Set HID B device" },
548 { "-joyBxaxis", SET_RESOURCE, CMDLINE_ATTRIB_NEED_ARGS,
549 NULL, NULL, "JoyBXAxis", NULL,
550 "<X,Y,Z,Rx,Ry,Rz>", "Set X Axis for HID B device" },
551 { "-joyByaxis", SET_RESOURCE, CMDLINE_ATTRIB_NEED_ARGS,
552 NULL, NULL, "JoyBYAxis", NULL,
553 "<X,Y,Z,Rx,Ry,Rz>", "Set Y Axis for HID B device" },
554 { "-joyBbuttons", SET_RESOURCE, CMDLINE_ATTRIB_NEED_ARGS,
555 NULL, NULL, "JoyBButtons", NULL,
556 "<f:af:l:r:u:d>", "Set Buttons for HID B device" },
557 { "-joyBautobuttons", SET_RESOURCE, CMDLINE_ATTRIB_NEED_ARGS,
558 NULL, NULL, "JoyBAutoButtons", NULL,
559 "<af1:af2:af1p:af1r:af2p:af2r>", "Set Auto Fire Buttons for HID B device" },
560 { "-joyBxthreshold", SET_RESOURCE, CMDLINE_ATTRIB_NEED_ARGS,
561 NULL, NULL, "JoyBXThreshold", NULL,
562 "<0-100>", "Set X Axis Threshold in Percent of HID B device" },
563 { "-joyBythreshold", SET_RESOURCE, CMDLINE_ATTRIB_NEED_ARGS,
564 NULL, NULL, "JoyBYThreshold", NULL,
565 "<0-100>", "Set Y Axis Threshold in Percent of HID B device" },
566 { "-joyAhatswitch", SET_RESOURCE, CMDLINE_ATTRIB_NEED_ARGS,
567 NULL, NULL, "JoyAHatSwitch", NULL,
568 "<0-n>", "Set Hat Switch for Joystick of HID A device" },
569 { "-joyBhatswitch", SET_RESOURCE, CMDLINE_ATTRIB_NEED_ARGS,
570 NULL, NULL, "JoyBHatSwitch", NULL,
571 "<0-n>", "Set Hat Switch for Joystick of HID B device" },
572 CMDLINE_LIST_END
573 };
574
575 static const cmdline_option_t joydev1cmdline_options[] =
576 {
577 { "-joydev1", SET_RESOURCE, CMDLINE_ATTRIB_NEED_ARGS,
578 NULL, NULL, "JoyDevice1", NULL,
579 "<0-5>", "Set device for joystick port 1" },
580 CMDLINE_LIST_END
581 };
582
583 static const cmdline_option_t joydev2cmdline_options[] =
584 {
585 { "-joydev2", SET_RESOURCE, CMDLINE_ATTRIB_NEED_ARGS,
586 NULL, NULL, "JoyDevice2", NULL,
587 "<0-5>", "Set device for joystick port 2" },
588 CMDLINE_LIST_END
589 };
590
591 static const cmdline_option_t joydev3cmdline_options[] =
592 {
593 { "-extrajoydev1", SET_RESOURCE, CMDLINE_ATTRIB_NEED_ARGS,
594 NULL, NULL, "JoyDevice3", NULL,
595 "<0-5>", "Set device for extra joystick port 1" },
596 CMDLINE_LIST_END
597 };
598
599 static const cmdline_option_t joydev4cmdline_options[] =
600 {
601 { "-extrajoydev2", SET_RESOURCE, CMDLINE_ATTRIB_NEED_ARGS,
602 NULL, NULL, "JoyDevice4", NULL,
603 "<0-5>", "Set device for extra joystick port 2" },
604 CMDLINE_LIST_END
605 };
606
607 static const cmdline_option_t joydev5cmdline_options[] =
608 {
609 { "-extrajoydev3", SET_RESOURCE, CMDLINE_ATTRIB_NEED_ARGS,
610 NULL, NULL, "JoyDevice5", NULL,
611 "<0-5>", "Set device for extra joystick port 3" },
612 CMDLINE_LIST_END
613 };
614
joy_arch_cmdline_options_init(void)615 int joy_arch_cmdline_options_init(void)
616 {
617 int num_ports = 0, num_extra_ports = 0;
618
619 if (joyport_get_port_name(JOYPORT_1)) {
620 if (cmdline_register_options(joydev1cmdline_options) < 0) {
621 return -1;
622 }
623 num_ports++;
624 }
625 if (joyport_get_port_name(JOYPORT_2)) {
626 if (cmdline_register_options(joydev2cmdline_options) < 0) {
627 return -1;
628 }
629 num_ports++;
630 }
631 if (joyport_get_port_name(JOYPORT_3)) {
632 if (cmdline_register_options(joydev3cmdline_options) < 0) {
633 return -1;
634 }
635 num_extra_ports++;
636 }
637 if (joyport_get_port_name(JOYPORT_4)) {
638 if (cmdline_register_options(joydev4cmdline_options) < 0) {
639 return -1;
640 }
641 num_extra_ports++;
642 }
643 if (joyport_get_port_name(JOYPORT_5)) {
644 if (cmdline_register_options(joydev5cmdline_options) < 0) {
645 return -1;
646 }
647 num_extra_ports++;
648 }
649
650 joy_num_ports = num_ports;
651 joy_num_extra_ports = num_extra_ports;
652
653 return cmdline_register_options(cmdline_options);
654 }
655
656 /* ----- Setup Joystick Descriptor ---------------------------------------- */
657
joy_calc_threshold(int min,int max,int threshold,int * t_min,int * t_max)658 void joy_calc_threshold(int min, int max, int threshold, int *t_min, int *t_max)
659 {
660 int range = max - min;;
661 int safe = range * threshold / 200;
662 *t_min = min + safe;
663 *t_max = max - safe;
664 }
665
666 static const char *desc[] = { "horizontal", "vertical" };
667
setup_axis_mapping(joystick_descriptor_t * joy)668 static void setup_axis_mapping(joystick_descriptor_t *joy)
669 {
670 int i;
671
672 for (i = 0; i < 2; i++) {
673 joy_axis_t *axis = &joy->axis[i];
674
675 /* map axis name to HID usage */
676 int usage = joy_hid_get_axis_usage(axis->name);
677 if (usage == -1) {
678 log_message(LOG_DEFAULT, "mac_joy: %s axis not mapped",
679 desc[i]);
680 axis->mapped = 0;
681 } else {
682 /* if axis range is invalid then try to read axis range from device */
683 if (axis->min >= axis->max) {
684 int err = joy_hid_reset_axis_range(joy, i, usage, axis->logical);
685 if (err == 0) {
686 log_message(LOG_DEFAULT, "mac_joy: %s axis mapped to HID '%s': reset range",
687 desc[i], axis->name);
688 }
689 }
690
691 /* try to map axis with given HID usage */
692 int err = joy_hid_assign_axis(joy, i, usage, axis->logical);
693 if (err == 0) {
694 log_message(LOG_DEFAULT, "mac_joy: %s axis mapped to HID '%s'. threshold=%d -> min=%d max=%d [%d;%d] %s",
695 desc[i], axis->name,
696 axis->threshold, axis->min_threshold, axis->max_threshold,
697 axis->min, axis->max, axis->logical ? "logical" : "physical");
698 } else {
699 log_message(LOG_DEFAULT, "mac_joy: NO %s axis not found on HID device",
700 axis->name);
701 }
702 }
703 }
704 }
705
joy_reset_axis_range(joystick_descriptor_t * joy,int id)706 void joy_reset_axis_range(joystick_descriptor_t *joy, int id)
707 {
708 joy_axis_t *axis = &joy->axis[id];
709 int old_min = axis->min;
710 int old_max = axis->max;
711
712 int new_min, new_max;
713 joy_hid_info_axis(joy, id, &new_min, &new_max, axis->logical);
714
715 int changed = 0;
716 if (new_min != old_min) {
717 axis->min = new_min;
718 changed = 1;
719 }
720 if (new_max != old_max) {
721 axis->max = new_max;
722 changed = 1;
723 }
724 if (changed) {
725 log_message(LOG_DEFAULT, "mac_joy: %s axis mapped to HID '%s': reset range",
726 desc[id], axis->name);
727 setup_axis_mapping(joy);
728 }
729 }
730
setup_button_mapping(joystick_descriptor_t * joy)731 static void setup_button_mapping(joystick_descriptor_t *joy)
732 {
733 int i;
734 int ids[HID_NUM_BUTTONS];
735
736 /* preset button id */
737 for (i = 0; i < HID_NUM_BUTTONS; i++) {
738 ids[i] = (i<2) ? i+1 : HID_INVALID_BUTTON;
739 }
740
741 /* decode button mapping resource */
742 if (joy->button_mapping && strlen(joy->button_mapping) > 0) {
743 if (sscanf(joy->button_mapping, "%d:%d:%d:%d:%d:%d", &ids[0], &ids[1], &ids[2], &ids[3], &ids[4], &ids[5]) != 6) {
744 log_message(LOG_DEFAULT, "mac_joy: invalid button mapping!");
745 }
746 }
747
748 /* try to map buttons in HID device */
749 for (i = 0; i < HID_NUM_BUTTONS; i++) {
750 joy->buttons[i].id = ids[i];
751 joy->buttons[i].press = 0;
752 joy->buttons[i].release = 0;
753 if (ids[i] != HID_INVALID_BUTTON) {
754 if (joy_hid_assign_button(joy, i, ids[i]) != 0) {
755 log_message(LOG_DEFAULT, "mac_joy: NO button %d on HID device!", ids[i]);
756 }
757 }
758 }
759
760 /* show button mapping */
761 log_message(LOG_DEFAULT, "mac_joy: buttons: fire_a=%d fire_b=%d left=%d right=%d up=%d down=%d",
762 ids[HID_FIRE], ids[HID_ALT_FIRE], ids[HID_LEFT], ids[HID_RIGHT], ids[HID_UP], ids[HID_DOWN]);
763 }
764
setup_auto_button_mapping(joystick_descriptor_t * joy)765 static void setup_auto_button_mapping(joystick_descriptor_t *joy)
766 {
767 int i;
768 int ids[HID_NUM_AUTO_BUTTONS * 3];
769
770 /* preset button id */
771 int offset = HID_NUM_AUTO_BUTTONS;
772 for (i = 0; i < HID_NUM_AUTO_BUTTONS; i++) {
773 ids[i] = i+3;
774 ids[offset++] = 5 * (i+1);
775 ids[offset++] = 5 * (i+1);
776 }
777
778 /* decode auto button mapping resource */
779 if (joy->auto_button_mapping && strlen(joy->auto_button_mapping) > 0) {
780 if (sscanf(joy->auto_button_mapping, "%d:%d:%d:%d:%d:%d", &ids[0], &ids[1], &ids[2], &ids[3], &ids[4], &ids[5]) != 6) {
781 log_message(LOG_DEFAULT, "mac_joy: invalid auto button mapping!");
782 }
783 }
784
785 /* try to map auto buttons in HID device */
786 offset = HID_NUM_AUTO_BUTTONS;
787 for (i = 0; i < HID_NUM_AUTO_BUTTONS; i++) {
788 int b = i + HID_NUM_BUTTONS; /* auto buttons are behind buttons */
789 joy->buttons[b].id = ids[i];
790 joy->buttons[b].press = ids[offset++];
791 joy->buttons[b].release = ids[offset++];
792 if (ids[i] != HID_INVALID_BUTTON) {
793 if (joy_hid_assign_button(joy, b, ids[i]) != 0) {
794 log_message(LOG_DEFAULT, "mac_joy: NO auto button %d on HID device!", ids[i]);
795 }
796 }
797 }
798
799 /* show button mapping */
800 log_message(LOG_DEFAULT, "mac_joy: autofire buttons: autofire_a=%d [press=%d release=%d] autofire_b=%d [press=%d release=%d]",
801 ids[0], ids[2], ids[3], ids[1], ids[4], ids[5]);
802 }
803
setup_hat_switch_mapping(joystick_descriptor_t * joy)804 static void setup_hat_switch_mapping(joystick_descriptor_t *joy)
805 {
806 int id = joy->hat_switch.id;
807 if (id != HID_INVALID_BUTTON) {
808 if (joy_hid_assign_hat_switch(joy, id) == 0) {
809 log_message(LOG_DEFAULT, "mac_joy: mapped hat switch: %d", id);
810 } else {
811 log_message(LOG_DEFAULT, "mac_joy: NO hat switch %d on HID device!", id);
812 joy->hat_switch.mapped = 0;
813 }
814 } else {
815 joy->hat_switch.mapped = 0;
816 log_message(LOG_DEFAULT, "mac_joy: hat switch not mapped");
817 }
818 }
819
820 /* determine if the given device matches the joystick descriptor */
match_joystick(joystick_descriptor_t * joy,joy_hid_device_t * dev)821 static int match_joystick(joystick_descriptor_t *joy, joy_hid_device_t *dev)
822 {
823 /* match by device name */
824 if (joy->device_name && strlen(joy->device_name) > 0) {
825 int vid, pid, serial;
826 if (sscanf(joy->device_name, "%x:%x:%d", &vid, &pid, &serial) != 3) {
827 return 0;
828 }
829 return (vid == dev->vendor_id) && (pid == dev->product_id) && (dev->serial == serial);
830 }
831 /* no match */
832 return 0;
833 }
834
setup_joystick(joystick_descriptor_t * joy,joy_hid_device_t * dev,const char * desc)835 static void setup_joystick(joystick_descriptor_t *joy, joy_hid_device_t *dev, const char *desc)
836 {
837 if (joy_hid_map_device(joy, dev) >= 0) {
838 log_message(LOG_DEFAULT, "mac_joy: set up %s HID joystick (%d buttons, %d axis, %d hat switches)",
839 desc, joy->num_hid_buttons, joy->num_hid_axis, joy->num_hid_hat_switches);
840 setup_axis_mapping(joy);
841 setup_button_mapping(joy);
842 setup_auto_button_mapping(joy);
843 setup_hat_switch_mapping(joy);
844 } else {
845 log_message(LOG_DEFAULT, "mac_joy: ERROR setting up %s HID joystick", desc);
846 }
847 }
848
849 /* is the joystick auto assignable? */
do_auto_assign(joystick_descriptor_t * joy)850 static int do_auto_assign(joystick_descriptor_t *joy)
851 {
852 return ((joy->device_name == NULL) || (strlen(joy->device_name) == 0));
853 }
854
setup_auto(void)855 static void setup_auto(void)
856 {
857 int auto_assign_a = do_auto_assign(&joy_a);
858 int auto_assign_b = do_auto_assign(&joy_b);
859 int i;
860 int num_devices;
861 const joy_hid_device_array_t *devices;
862
863 /* unmap both joysticks */
864 joy_a.mapped = 0;
865 joy_b.mapped = 0;
866
867 /* query device list */
868 devices = joy_hid_get_devices();
869 if (devices == NULL) {
870 log_message(LOG_DEFAULT, "mac_joy: can't find any HID devices!");
871 return;
872 }
873 num_devices = devices->num_devices;
874
875 /* walk through all enumerated devices */
876 log_message(LOG_DEFAULT, "mac_joy: (auto) found %d HID devices. HID A='%s' B='%s'",
877 num_devices, joy_a.device_name, joy_b.device_name);
878 for (i = 0; i < num_devices; i++) {
879 joy_hid_device_t *dev = &devices->devices[i];
880
881 log_message(LOG_DEFAULT, "mac_joy: found #%d joystick/gamepad: %04x:%04x:%d %s",
882 i, dev->vendor_id, dev->product_id, dev->serial, dev->product_name);
883
884 /* query joy A */
885 int assigned = 0;
886 if (!auto_assign_a && match_joystick(&joy_a, dev)) {
887 setup_joystick(&joy_a, dev, "matched A");
888 assigned = 1;
889 }
890 /* query joy B */
891 if (!auto_assign_b && match_joystick(&joy_b, dev)) {
892 setup_joystick(&joy_b, dev, "matched B");
893 assigned = 1;
894 }
895
896 if (!assigned) {
897 /* auto assign a */
898 if (auto_assign_a && (joy_a.mapped == 0)) {
899 setup_joystick(&joy_a, dev, "auto-assigned A");
900 }
901 /* auto assign b */
902 else if (auto_assign_b && (joy_b.mapped == 0)) {
903 setup_joystick(&joy_b, dev, "auto-assigned B");
904 }
905 }
906 }
907
908 /* check if matched */
909 if (!auto_assign_a && (joy_a.mapped == 0)) {
910 log_message(LOG_DEFAULT, "mac_joy: joystick A not matched!");
911 }
912 if (!auto_assign_b && (joy_b.mapped == 0)) {
913 log_message(LOG_DEFAULT, "mac_joy: joystick B not matched!");
914 }
915 log_message(LOG_DEFAULT, "mac_joy: (auto) done");
916 }
917
918 /* ----- API ----- */
919
920 /* check if a new joystick mapping is valid */
joy_arch_set_device(int port,int new_dev)921 int joy_arch_set_device(int port, int new_dev)
922 {
923 switch (new_dev) {
924 case JOYDEV_NONE:
925 case JOYDEV_NUMPAD:
926 case JOYDEV_KEYSET1:
927 case JOYDEV_KEYSET2:
928 case JOYDEV_HID_0:
929 case JOYDEV_HID_1:
930 break;
931 default:
932 return -1;
933 }
934
935 return 0;
936 }
937
938 /* helper for UI to reload device list */
joy_reload_device_list(void)939 void joy_reload_device_list(void)
940 {
941 if (joy_hid_reload() < 0) {
942 joy_done_init = 0;
943 log_message(LOG_DEFAULT, "mac_joy: ERROR loading HID device list! Disabling devices. Try again!");
944 } else {
945 const joy_hid_device_array_t *devices = joy_hid_get_devices();
946 log_message(LOG_DEFAULT, "mac_joy: reloaded HID device list with %s. found %d devices",
947 devices->driver_name, devices->num_devices);
948 setup_auto();
949 }
950 }
951
952 /* query for available joysticks and set them up */
joy_arch_init(void)953 int joy_arch_init(void)
954 {
955 if (joy_hid_init() < 0) {
956 return 0;
957 }
958
959 joy_done_init = 1;
960
961 /* print initial device list info */
962 const joy_hid_device_array_t *devices = joy_hid_get_devices();
963 log_message(LOG_DEFAULT, "mac_joy: loaded HID device list with %s. found %d devices",
964 devices->driver_name, devices->num_devices);
965
966 /* now assign HID joystick A,B if available */
967 setup_auto();
968
969 return 0;
970 }
971
972 /* close the device */
joystick_close(void)973 void joystick_close(void)
974 {
975 joy_hid_unmap_device(&joy_a);
976 joy_hid_unmap_device(&joy_b);
977 joy_hid_exit();
978
979 lib_free(joy_a.device_name);
980 lib_free(joy_a.axis[HID_X_AXIS].name);
981 lib_free(joy_a.axis[HID_Y_AXIS].name);
982 lib_free(joy_a.button_mapping);
983 lib_free(joy_a.auto_button_mapping);
984 lib_free(joy_b.device_name);
985 lib_free(joy_b.axis[HID_X_AXIS].name);
986 lib_free(joy_b.axis[HID_Y_AXIS].name);
987 lib_free(joy_b.button_mapping);
988 lib_free(joy_b.auto_button_mapping);
989 }
990
991 /* ----- Read Joystick ----- */
992
read_button(joystick_descriptor_t * joy,int id,uint8_t resValue)993 static uint8_t read_button(joystick_descriptor_t *joy, int id, uint8_t resValue)
994 {
995 /* button not mapped? */
996 if (joy->buttons[id].mapped == 0) {
997 return 0;
998 }
999
1000 int value;
1001 if (joy_hid_read_button(joy, id, &value) != 0) {
1002 return 0;
1003 }
1004
1005 return value ? resValue : 0;
1006 }
1007
read_auto_button(joystick_descriptor_t * joy,int id,uint8_t resValue)1008 static uint8_t read_auto_button(joystick_descriptor_t *joy, int id, uint8_t resValue)
1009 {
1010 /* button not mapped? */
1011 joy_button_t *button = &joy->buttons[id];
1012
1013 if (button->mapped == 0) {
1014 return 0;
1015 }
1016
1017 int value;
1018 if (joy_hid_read_button(joy, id, &value) != 0) {
1019 return 0;
1020 }
1021
1022 /* perform auto fire operation */
1023 int result = 0;
1024 if (value) {
1025 if(button->counter < button->press) {
1026 result = resValue;
1027 }
1028 button->counter ++;
1029 if (button->counter == (button->press + button->release)) {
1030 button->counter = 0;
1031 }
1032 } else {
1033 button->counter = 0;
1034 }
1035 return result;
1036 }
1037
read_axis(joystick_descriptor_t * joy,int id,uint8_t min,uint8_t max)1038 static uint8_t read_axis(joystick_descriptor_t *joy, int id, uint8_t min, uint8_t max)
1039 {
1040 joy_axis_t *axis = &joy->axis[id];
1041 if (axis->mapped == 0) {
1042 return 0;
1043 }
1044
1045 int value;
1046 if (joy_hid_read_axis(joy, id, &value, axis->logical) != 0) {
1047 return 0;
1048 }
1049
1050 if (value < axis->min_threshold) {
1051 return min;
1052 } else if (value > axis->max_threshold) {
1053 return max;
1054 } else {
1055 return 0;
1056 }
1057 }
1058
read_hat_switch(joystick_descriptor_t * joy)1059 static uint8_t read_hat_switch(joystick_descriptor_t *joy)
1060 {
1061 static const int map_hid_to_joy[8] = {
1062 1, /* 1=N */
1063 1+8, /* 2=NE */
1064 8, /* 3=E */
1065 8+2, /* 4=SE */
1066 2, /* 5=S */
1067 4+2, /* 6=SW */
1068 4, /* 7=W */
1069 1+4, /* 8=NW */
1070 };
1071
1072 if (joy->hat_switch.mapped == 0) {
1073 return 0;
1074 }
1075
1076 int value;
1077 if (joy_hid_read_hat_switch(joy, &value) != 0) {
1078 return 0;
1079 }
1080
1081 /* valid hat directions: 1..8 */
1082 if ((value < 0) || (value > 7)) {
1083 return 0;
1084 }
1085
1086 return map_hid_to_joy[value];
1087 }
1088
read_joystick(joystick_descriptor_t * joy)1089 static uint8_t read_joystick(joystick_descriptor_t *joy)
1090 {
1091 /* read buttons */
1092 uint8_t joy_bits = read_button(joy, HID_FIRE, 16) |
1093 read_button(joy, HID_ALT_FIRE, 16) |
1094 read_button(joy, HID_LEFT, 4) |
1095 read_button(joy, HID_RIGHT, 8) |
1096 read_button(joy, HID_UP, 1) |
1097 read_button(joy, HID_DOWN, 2) |
1098 read_auto_button(joy, HID_AUTO_FIRE, 16) |
1099 read_auto_button(joy, HID_AUTO_ALT_FIRE, 16);
1100
1101 /* axis */
1102 joy_bits |= read_axis(joy, HID_X_AXIS, 4, 8) |
1103 read_axis(joy, HID_Y_AXIS, 1, 2);
1104
1105 /* hat switch */
1106 joy_bits |= read_hat_switch(joy);
1107
1108 return joy_bits;
1109 }
1110
1111 /* poll joystick */
joystick(void)1112 void joystick(void)
1113 {
1114 int i;
1115
1116 /* handle both virtual cbm joystick ports */
1117 for (i = 0; i < 5; i++) {
1118 /* what kind of device is connected to the virtual port? */
1119 int joy_port = joystick_port_map[i];
1120
1121 /* is HID joystick A mapped? */
1122 if (joy_port == JOYDEV_HID_0) {
1123 if (joy_a.mapped) {
1124 uint8_t joy_bits = read_joystick(&joy_a);
1125 joystick_set_value_absolute(i + 1, joy_bits);
1126 }
1127 }
1128 /* is HID joystick B mapped? */
1129 else if (joy_port == JOYDEV_HID_1) {
1130 if (joy_b.mapped) {
1131 uint8_t joy_bits = read_joystick(&joy_b);
1132 joystick_set_value_absolute(i + 1, joy_bits);
1133 }
1134 }
1135 }
1136 }
1137
1138 #else
1139
joystick_close(void)1140 void joystick_close(void)
1141 {
1142 /* NOP */
1143 }
1144
1145 #endif /* HAS_JOYSTICK */
1146
1147 #endif /* MACOSX_SUPPORT */
1148