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