1 /*         ______   ___    ___
2  *        /\  _  \ /\_ \  /\_ \
3  *        \ \ \L\ \\//\ \ \//\ \      __     __   _ __   ___
4  *         \ \  __ \ \ \ \  \ \ \   /'__`\ /'_ `\/\`'__\/ __`\
5  *          \ \ \/\ \ \_\ \_ \_\ \_/\  __//\ \L\ \ \ \//\ \L\ \
6  *           \ \_\ \_\/\____\/\____\ \____\ \____ \ \_\\ \____/
7  *            \/_/\/_/\/____/\/____/\/____/\/___L\ \/_/ \/___/
8  *                                           /\____/
9  *                                           \_/__/
10  *
11  *      DOS joystick routines.
12  *
13  *      By Shawn Hargreaves.
14  *
15  *      Based on code provided by Jonathan Tarbox and Marcel de Kogel.
16  *
17  *      CH Flightstick Pro and Logitech Wingman Extreme
18  *      support by Fabian Nunez.
19  *
20  *      Matthew Bowie added support for 4-button joysticks.
21  *
22  *      Richard Mitton added support for 6-button joysticks.
23  *
24  *      Stefan Eilert added support for dual joysticks.
25  *
26  *      See readme.txt for copyright information.
27  */
28 
29 
30 #include "allegro.h"
31 #include "allegro/internal/aintern.h"
32 #include "allegro/platform/aintdos.h"
33 
34 #ifndef ALLEGRO_DOS
35    #error something is wrong with the makefile
36 #endif
37 
38 
39 
40 /* driver functions */
41 static int joy_init(void);
42 static void joy_exit(void);
43 static int joy_poll(void);
44 static int joy_save_data(void);
45 static int joy_load_data(void);
46 static AL_CONST char *joy_calibrate_name(int n);
47 static int joy_calibrate(int n);
48 
49 static int poll(int *x, int *y, int *x2, int *y2, int poll_mask);
50 
51 
52 
53 #define JOYSTICK_DRIVER_CONTENTS       \
54    joy_init,                           \
55    joy_exit,                           \
56    joy_poll,                           \
57    joy_save_data,                      \
58    joy_load_data,                      \
59    joy_calibrate_name,                 \
60    joy_calibrate
61 
62 
63 
64 JOYSTICK_DRIVER joystick_standard =
65 {
66    JOY_TYPE_STANDARD,
67    empty_string,
68    empty_string,
69    "Standard joystick",
70    JOYSTICK_DRIVER_CONTENTS
71 };
72 
73 
74 
75 JOYSTICK_DRIVER joystick_2pads =
76 {
77    JOY_TYPE_2PADS,
78    empty_string,
79    empty_string,
80    "Dual joysticks",
81    JOYSTICK_DRIVER_CONTENTS
82 };
83 
84 
85 
86 JOYSTICK_DRIVER joystick_4button =
87 {
88    JOY_TYPE_4BUTTON,
89    empty_string,
90    empty_string,
91    "4-button joystick",
92    JOYSTICK_DRIVER_CONTENTS
93 };
94 
95 
96 
97 JOYSTICK_DRIVER joystick_6button =
98 {
99    JOY_TYPE_6BUTTON,
100    empty_string,
101    empty_string,
102    "6-button joystick",
103    JOYSTICK_DRIVER_CONTENTS
104 };
105 
106 
107 
108 JOYSTICK_DRIVER joystick_8button =
109 {
110    JOY_TYPE_8BUTTON,
111    empty_string,
112    empty_string,
113    "8-button joystick",
114    JOYSTICK_DRIVER_CONTENTS
115 };
116 
117 
118 
119 JOYSTICK_DRIVER joystick_fspro =
120 {
121    JOY_TYPE_FSPRO,
122    empty_string,
123    empty_string,
124    "Flightstick Pro",
125    JOYSTICK_DRIVER_CONTENTS
126 };
127 
128 
129 
130 JOYSTICK_DRIVER joystick_wingex =
131 {
132    JOY_TYPE_WINGEX,
133    empty_string,
134    empty_string,
135    "Wingman Extreme",
136    JOYSTICK_DRIVER_CONTENTS
137 };
138 
139 
140 
141 /* flags describing different variants of the basic joystick */
142 #define JDESC_STICK2             0x0001
143 #define JDESC_4BUTTON            0x0002
144 #define JDESC_6BUTTON            0x0004
145 #define JDESC_8BUTTON            0x0008
146 #define JDESC_Y2_THROTTLE        0x0010
147 #define JDESC_Y2_HAT             0x0020
148 #define JDESC_FSPRO_HAT          0x0040
149 
150 
151 
152 /* calibration state information */
153 #define JOYSTICK_CALIB_TL1             0x00010000
154 #define JOYSTICK_CALIB_BR1             0x00020000
155 #define JOYSTICK_CALIB_TL2             0x00040000
156 #define JOYSTICK_CALIB_BR2             0x00080000
157 #define JOYSTICK_CALIB_THRTL_MIN       0x00100000
158 #define JOYSTICK_CALIB_THRTL_MAX       0x00200000
159 #define JOYSTICK_CALIB_HAT_CENTRE      0x00400000
160 #define JOYSTICK_CALIB_HAT_LEFT        0x00800000
161 #define JOYSTICK_CALIB_HAT_DOWN        0x01000000
162 #define JOYSTICK_CALIB_HAT_RIGHT       0x02000000
163 #define JOYSTICK_CALIB_HAT_UP          0x04000000
164 
165 #define JOYSTICK_CALIB_HAT    (JOYSTICK_CALIB_HAT_CENTRE |     \
166 			       JOYSTICK_CALIB_HAT_LEFT |       \
167 			       JOYSTICK_CALIB_HAT_DOWN |       \
168 			       JOYSTICK_CALIB_HAT_RIGHT |      \
169 			       JOYSTICK_CALIB_HAT_UP)
170 
171 
172 
173 /* driver state information */
174 static int joystick_flags = 0;
175 
176 static int joycentre_x, joycentre_y;
177 static int joyx_min, joyx_low_margin, joyx_high_margin, joyx_max;
178 static int joyy_min, joyy_low_margin, joyy_high_margin, joyy_max;
179 
180 static int joycentre2_x, joycentre2_y;
181 static int joyx2_min, joyx2_low_margin, joyx2_high_margin, joyx2_max;
182 static int joyy2_min, joyy2_low_margin, joyy2_high_margin, joyy2_max;
183 
184 static int joy_thr_min, joy_thr_max;
185 
186 static int joy_hat_pos[5], joy_hat_threshold[4];
187 
188 static int joy_old_x, joy_old_y;
189 static int joy2_old_x, joy2_old_y;
190 
191 
192 
193 /* masks indicating which axis to read */
194 #define MASK_1X      1
195 #define MASK_1Y      2
196 #define MASK_2X      4
197 #define MASK_2Y      8
198 
199 
200 
201 /* poll_mask:
202  *  Returns a mask indicating which axes to poll.
203  */
poll_mask(void)204 static int poll_mask(void)
205 {
206    int mask = MASK_1X | MASK_1Y;
207 
208    if (joystick_flags & (JDESC_STICK2 | JDESC_6BUTTON | JDESC_8BUTTON))
209       mask |= (MASK_2X | MASK_2Y);
210 
211    if (joystick_flags & (JDESC_Y2_THROTTLE | JDESC_Y2_HAT))
212       mask |= MASK_2Y;
213 
214    return mask;
215 }
216 
217 
218 
219 /* averaged_poll:
220  *  For calibration it is crucial that we get the right results, so we
221  *  average out several attempts.
222  */
averaged_poll(int * x,int * y,int * x2,int * y2,int mask)223 static int averaged_poll(int *x, int *y, int *x2, int *y2, int mask)
224 {
225    int x_tmp, y_tmp, x2_tmp, y2_tmp;
226    int x_total, y_total, x2_total, y2_total;
227    int c;
228 
229    #define AVERAGE_COUNT   4
230 
231    x_total = y_total = x2_total = y2_total = 0;
232 
233    for (c=0; c<AVERAGE_COUNT; c++) {
234       if (poll(&x_tmp, &y_tmp, &x2_tmp, &y2_tmp, mask) != 0)
235 	 return -1;
236 
237       x_total += x_tmp;
238       y_total += y_tmp;
239       x2_total += x2_tmp;
240       y2_total += y2_tmp;
241    }
242 
243    *x = x_total / AVERAGE_COUNT;
244    *y = y_total / AVERAGE_COUNT;
245    *x2 = x2_total / AVERAGE_COUNT;
246    *y2 = y2_total / AVERAGE_COUNT;
247 
248    return 0;
249 }
250 
251 
252 
253 /* recalc_calibration_flags:
254  *  Called after each calibration operation, to calculate what else might
255  *  need to be measured for the current hardware.
256  */
recalc_calibration_flags(void)257 static void recalc_calibration_flags(void)
258 {
259    #define FLAG_SET(n)  ((joystick_flags & (n)) == (n))
260 
261    /* stick 1 analogue input? */
262    if (FLAG_SET(JOYSTICK_CALIB_TL1 | JOYSTICK_CALIB_BR1)) {
263       joy[0].stick[0].flags |= JOYFLAG_ANALOGUE;
264       joy[0].stick[0].flags &= ~JOYFLAG_CALIB_ANALOGUE;
265    }
266    else {
267       joy[0].stick[0].flags &= ~JOYFLAG_ANALOGUE;
268       joy[0].stick[0].flags |= JOYFLAG_CALIB_ANALOGUE;
269    }
270 
271    /* stick 2 analogue input? */
272    if (joystick_flags & JDESC_STICK2) {
273       if (FLAG_SET(JOYSTICK_CALIB_TL2 | JOYSTICK_CALIB_BR2)) {
274 	 joy[1].stick[0].flags |= JOYFLAG_ANALOGUE;
275 	 joy[1].stick[0].flags &= ~JOYFLAG_CALIB_ANALOGUE;
276       }
277       else {
278 	 joy[1].stick[0].flags &= ~JOYFLAG_ANALOGUE;
279 	 joy[1].stick[0].flags |= JOYFLAG_CALIB_ANALOGUE;
280       }
281    }
282 
283    /* Wingman Extreme hat input? */
284    if (joystick_flags & JDESC_Y2_HAT) {
285       if (FLAG_SET(JOYSTICK_CALIB_HAT)) {
286 	 joy[0].stick[1].flags |= JOYFLAG_DIGITAL;
287 	 joy[0].stick[1].flags &= ~JOYFLAG_CALIB_DIGITAL;
288       }
289       else {
290 	 joy[0].stick[1].flags &= ~JOYFLAG_DIGITAL;
291 	 joy[0].stick[1].flags |= JOYFLAG_CALIB_DIGITAL;
292       }
293    }
294 
295    /* FSPro throttle input? */
296    if (joystick_flags & JDESC_Y2_THROTTLE) {
297       if (FLAG_SET(JOYSTICK_CALIB_THRTL_MIN | JOYSTICK_CALIB_THRTL_MAX)) {
298 	 joy[0].stick[2].flags |= JOYFLAG_ANALOGUE;
299 	 joy[0].stick[2].flags &= ~JOYFLAG_CALIB_ANALOGUE;
300       }
301       else {
302 	 joy[0].stick[2].flags &= ~JOYFLAG_ANALOGUE;
303 	 joy[0].stick[2].flags |= JOYFLAG_CALIB_ANALOGUE;
304       }
305    }
306 }
307 
308 
309 
310 /* joy_init:
311  *  Initialises the driver.
312  */
joy_init(void)313 static int joy_init(void)
314 {
315    int i;
316 
317    /* store info about the stick type */
318    switch (_joy_type) {
319 
320       case JOY_TYPE_STANDARD:
321 	 joystick_flags = 0;
322 	 break;
323 
324       case JOY_TYPE_2PADS:
325 	 joystick_flags = JDESC_STICK2;
326 	 break;
327 
328       case JOY_TYPE_4BUTTON:
329 	 joystick_flags = JDESC_4BUTTON;
330 	 break;
331 
332       case JOY_TYPE_6BUTTON:
333 	 joystick_flags = JDESC_4BUTTON | JDESC_6BUTTON;
334 	 break;
335 
336       case JOY_TYPE_8BUTTON:
337 	 joystick_flags = JDESC_4BUTTON | JDESC_8BUTTON;
338 	 break;
339 
340       case JOY_TYPE_FSPRO:
341 	 joystick_flags = JDESC_4BUTTON | JDESC_Y2_THROTTLE | JDESC_FSPRO_HAT;
342 	 break;
343 
344       case JOY_TYPE_WINGEX:
345 	 joystick_flags = JDESC_4BUTTON | JDESC_Y2_HAT;
346 	 break;
347 
348       default:
349 	 return -1;
350    }
351 
352    joy_old_x = joy_old_y = 0;
353    joy2_old_x = joy2_old_y = 0;
354 
355    /* make sure the hardware is really present */
356    if (averaged_poll(&joycentre_x, &joycentre_y, &joycentre2_x, &joycentre2_y, poll_mask()) != 0)
357       return -1;
358 
359    /* fill in the joystick structure */
360    num_joysticks = (joystick_flags & JDESC_STICK2) ? 2 : 1;
361 
362    joy[0].flags = JOYFLAG_DIGITAL;
363 
364    /* how many buttons? */
365    if (joystick_flags & JDESC_8BUTTON)
366       joy[0].num_buttons = 8;
367    else if (joystick_flags & JDESC_6BUTTON)
368       joy[0].num_buttons = 6;
369    else if (joystick_flags & JDESC_4BUTTON)
370       joy[0].num_buttons = 4;
371    else
372       joy[0].num_buttons = 2;
373 
374    /* main analogue stick */
375    joy[0].stick[0].flags = JOYFLAG_DIGITAL | JOYFLAG_SIGNED;
376    joy[0].stick[0].num_axis = 2;
377    joy[0].stick[0].axis[0].name = get_config_text("X");
378    joy[0].stick[0].axis[1].name = get_config_text("Y");
379    joy[0].stick[0].name = get_config_text("Stick");
380    joy[0].num_sticks = 1;
381 
382    /* hat control? */
383    if (joystick_flags & (JDESC_FSPRO_HAT | JDESC_Y2_HAT)) {
384       joy[0].stick[joy[0].num_sticks].flags = JOYFLAG_DIGITAL | JOYFLAG_SIGNED;
385       joy[0].stick[joy[0].num_sticks].num_axis = 2;
386       joy[0].stick[joy[0].num_sticks].axis[0].name = get_config_text("X");
387       joy[0].stick[joy[0].num_sticks].axis[1].name = get_config_text("Y");
388       joy[0].stick[joy[0].num_sticks].name = get_config_text("Hat");
389       joy[0].num_sticks++;
390    }
391 
392    /* throttle control? */
393    if (joystick_flags & JDESC_Y2_THROTTLE) {
394       joy[0].stick[joy[0].num_sticks].flags = JOYFLAG_UNSIGNED;
395       joy[0].stick[joy[0].num_sticks].num_axis = 1;
396       joy[0].stick[joy[0].num_sticks].axis[0].name = get_config_text("Throttle");
397       joy[0].stick[joy[0].num_sticks].name = get_config_text("Throttle");
398       joy[0].num_sticks++;
399    }
400 
401    /* clone everything for a second joystick? */
402    if (joystick_flags & JDESC_STICK2)
403       joy[1] = joy[0];
404 
405    /* fill in the button names */
406    for (i=0; i<2; i++) {
407       joy[i].button[0].name = get_config_text("B1");
408       joy[i].button[1].name = get_config_text("B2");
409 
410       if (joy[i].num_buttons > 2) {
411 	 joy[i].button[2].name = get_config_text("B3");
412 	 joy[i].button[3].name = get_config_text("B4");
413       }
414 
415       if (joy[i].num_buttons > 4) {
416 	 joy[i].button[4].name = get_config_text("B5");
417 	 joy[i].button[5].name = get_config_text("B6");
418       }
419 
420       if (joy[i].num_buttons > 6) {
421 	 joy[i].button[6].name = get_config_text("B7");
422 	 joy[i].button[7].name = get_config_text("B8");
423       }
424    }
425 
426    recalc_calibration_flags();
427 
428    return 0;
429 }
430 
431 
432 
433 /* joy_exit:
434  *  Shuts down the driver.
435  */
joy_exit(void)436 static void joy_exit(void)
437 {
438    joystick_flags = 0;
439 }
440 
441 
442 
443 /* sort_out_middle_values:
444  *  Sets up the values used by sort_out_analogue() to create a 'dead'
445  *  region in the centre of the joystick range.
446  */
sort_out_middle_values(int n)447 static void sort_out_middle_values(int n)
448 {
449    if (n == 0) {
450       joyx_low_margin  = joycentre_x - (joycentre_x - joyx_min) / 8;
451       joyx_high_margin = joycentre_x + (joyx_max - joycentre_x) / 8;
452       joyy_low_margin  = joycentre_y - (joycentre_y - joyy_min) / 8;
453       joyy_high_margin = joycentre_y + (joyy_max - joycentre_y) / 8;
454    }
455    else {
456       joyx2_low_margin  = joycentre2_x - (joycentre2_x - joyx2_min) / 8;
457       joyx2_high_margin = joycentre2_x + (joyx2_max - joycentre2_x) / 8;
458       joyy2_low_margin  = joycentre2_y - (joycentre2_y - joyy2_min) / 8;
459       joyy2_high_margin = joycentre2_y + (joyy2_max - joycentre2_y) / 8;
460    }
461 }
462 
463 
464 
465 /* calibrate_corner:
466  *  For analogue access to the joystick, this is used to measure the top
467  *  left and bottom right corner positions.
468  */
calibrate_corner(int stick,int corner)469 static int calibrate_corner(int stick, int corner)
470 {
471    int flag, other, ret, nowhere;
472 
473    if (stick == 0) {
474       /* stick 1 calibration */
475       flag = (corner) ? JOYSTICK_CALIB_BR1 : JOYSTICK_CALIB_TL1;
476       other = (corner) ? JOYSTICK_CALIB_TL1 : JOYSTICK_CALIB_BR1;
477 
478       if (corner)
479 	 ret = averaged_poll(&joyx_max, &joyy_max, &nowhere, &nowhere, MASK_1X | MASK_1Y);
480       else
481 	 ret = averaged_poll(&joyx_min, &joyy_min, &nowhere, &nowhere, MASK_1X | MASK_1Y);
482    }
483    else {
484       /* stick 2 calibration */
485       flag = (corner) ? JOYSTICK_CALIB_BR2 : JOYSTICK_CALIB_TL2;
486       other = (corner) ? JOYSTICK_CALIB_TL2 : JOYSTICK_CALIB_BR2;
487 
488       if (corner)
489 	 ret = averaged_poll(&nowhere, &nowhere, &joyx2_max, &joyy2_max, MASK_2X | MASK_2Y);
490       else
491 	 ret = averaged_poll(&nowhere, &nowhere, &joyx2_min, &joyy2_min, MASK_2X | MASK_2Y);
492    }
493 
494    if (ret != 0) {
495       joystick_flags &= ~flag;
496       return -1;
497    }
498 
499    joystick_flags |= flag;
500 
501    /* once we've done both corners, we are ready for full analogue input */
502    if (joystick_flags & other) {
503       sort_out_middle_values(stick);
504       recalc_calibration_flags();
505    }
506 
507    return 0;
508 }
509 
510 
511 
512 /* calibrate_joystick_tl:
513  *  For backward compatibility with the old API.
514  */
calibrate_joystick_tl(void)515 int calibrate_joystick_tl(void)
516 {
517    int ret;
518 
519    if (!_joystick_installed)
520       return -1;
521 
522    ret = calibrate_corner(0, 0);
523 
524    if ((ret == 0) && (joystick_flags & JDESC_STICK2))
525       ret = calibrate_corner(1, 0);
526 
527    return ret;
528 }
529 
530 
531 
532 /* calibrate_joystick_br:
533  *  For backward compatibility with the old API.
534  */
calibrate_joystick_br(void)535 int calibrate_joystick_br(void)
536 {
537    int ret;
538 
539    if (!_joystick_installed)
540       return -1;
541 
542    ret = calibrate_corner(0, 1);
543 
544    if ((ret == 0) && (joystick_flags & JDESC_STICK2))
545       ret = calibrate_corner(1, 1);
546 
547    return ret;
548 }
549 
550 
551 
552 /* calibrate_joystick_throttle_min:
553  *  For analogue access to the FSPro's throttle, call this after
554  *  initialise_joystick(), with the throttle at the "minimum" extreme
555  *  (the user decides whether this is all the way forwards or all the
556  *  way back), and also call calibrate_joystick_throttle_max().
557  */
calibrate_joystick_throttle_min(void)558 int calibrate_joystick_throttle_min(void)
559 {
560    int dummy;
561 
562    if (!_joystick_installed)
563       return -1;
564 
565    if (averaged_poll(&dummy, &dummy, &dummy, &joy_thr_min, MASK_2Y) != 0)
566       return -1;
567 
568    /* prevent division by zero errors if user miscalibrated */
569    if ((joystick_flags & JOYSTICK_CALIB_THRTL_MAX) && (joy_thr_min == joy_thr_max))
570      joy_thr_min = 255 - joy_thr_max;
571 
572    joystick_flags |= JOYSTICK_CALIB_THRTL_MIN;
573    recalc_calibration_flags();
574 
575    return 0;
576 }
577 
578 
579 
580 /* calibrate_joystick_throttle_max:
581  *  For analogue access to the FSPro's throttle, call this after
582  *  initialise_joystick(), with the throttle at the "maximum" extreme
583  *  (the user decides whether this is all the way forwards or all the
584  *  way back), and also call calibrate_joystick_throttle_min().
585  */
calibrate_joystick_throttle_max(void)586 int calibrate_joystick_throttle_max(void)
587 {
588    int dummy;
589 
590    if (!_joystick_installed)
591       return -1;
592 
593    if (averaged_poll(&dummy, &dummy, &dummy, &joy_thr_max, MASK_2Y) != 0)
594       return -1;
595 
596    /* prevent division by zero errors if user miscalibrated */
597    if ((joystick_flags & JOYSTICK_CALIB_THRTL_MIN) && (joy_thr_min == joy_thr_max))
598      joy_thr_max = 255 - joy_thr_min;
599 
600    joystick_flags |= JOYSTICK_CALIB_THRTL_MAX;
601    recalc_calibration_flags();
602 
603    return 0;
604 }
605 
606 
607 
608 /* calibrate_joystick_hat:
609  *  For access to the Wingman Extreme's hat (I think this will work on all
610  *  Thrustmaster compatible joysticks), call this after initialise_joystick(),
611  *  passing the JOY_HAT constant you wish to calibrate.
612  */
calibrate_joystick_hat(int direction)613 int calibrate_joystick_hat(int direction)
614 {
615    static int pos_const[] =
616    {
617       JOYSTICK_CALIB_HAT_CENTRE,
618       JOYSTICK_CALIB_HAT_LEFT,
619       JOYSTICK_CALIB_HAT_DOWN,
620       JOYSTICK_CALIB_HAT_RIGHT,
621       JOYSTICK_CALIB_HAT_UP
622    };
623 
624    int dummy, value;
625 
626    if ((direction > JOY_HAT_UP) || (!_joystick_installed))
627       return -1;
628 
629    if (averaged_poll(&dummy, &dummy, &dummy, &value, MASK_2Y) != 0)
630       return -1;
631 
632    joy_hat_pos[direction] = value;
633    joystick_flags |= pos_const[direction];
634 
635    /* when all directions have been calibrated, calculate deltas */
636    if ((joystick_flags & JOYSTICK_CALIB_HAT) == JOYSTICK_CALIB_HAT) {
637       joy_hat_threshold[0] = (joy_hat_pos[0] + joy_hat_pos[1]) / 2;
638       joy_hat_threshold[1] = (joy_hat_pos[1] + joy_hat_pos[2]) / 2;
639       joy_hat_threshold[2] = (joy_hat_pos[2] + joy_hat_pos[3]) / 2;
640       joy_hat_threshold[3] = (joy_hat_pos[3] + joy_hat_pos[4]) / 2;
641 
642       recalc_calibration_flags();
643    }
644 
645    return 0;
646 }
647 
648 
649 
650 /* sort_out_analogue:
651  *  There are a couple of problems with reading analogue input from the PC
652  *  joystick. For one thing, joysticks tend not to centre repeatably, so
653  *  we need a small 'dead' zone in the middle. Also a lot of joysticks aren't
654  *  linear, so the positions less than centre need to be handled differently
655  *  to those above the centre.
656  */
sort_out_analogue(int x,int min,int low_margin,int high_margin,int max)657 static int sort_out_analogue(int x, int min, int low_margin, int high_margin, int max)
658 {
659    if (x < min) {
660       return -128;
661    }
662    else if (x < low_margin) {
663       return -128 + (x - min) * 128 / (low_margin - min);
664    }
665    else if (x <= high_margin) {
666       return 0;
667    }
668    else if (x <= max) {
669       return 128 - (max - x) * 128 / (max - high_margin);
670    }
671    else
672       return 128;
673 }
674 
675 
676 
677 /* joy_poll:
678  *  Updates the joystick status variables.
679  */
joy_poll(void)680 static int joy_poll(void)
681 {
682    int x, y, x2, y2, i;
683    unsigned char status;
684 
685    /* read the hardware */
686    if (poll(&x, &y, &x2, &y2, poll_mask()) != 0)
687       return -1;
688 
689    status = inportb(0x201);
690 
691    /* stick 1 position */
692    if ((ABS(x-joy_old_x) <= x/4) && (ABS(y-joy_old_y) <= y/4)) {
693       if ((joystick_flags & JOYSTICK_CALIB_TL1) && (joystick_flags & JOYSTICK_CALIB_BR1)) {
694 	 joy[0].stick[0].axis[0].pos = sort_out_analogue(x, joyx_min, joyx_low_margin, joyx_high_margin, joyx_max);
695 	 joy[0].stick[0].axis[1].pos = sort_out_analogue(y, joyy_min, joyy_low_margin, joyy_high_margin, joyy_max);
696       }
697       else {
698 	 joy[0].stick[0].axis[0].pos = x - joycentre_x;
699 	 joy[0].stick[0].axis[1].pos = y - joycentre_y;
700       }
701 
702       joy[0].stick[0].axis[0].d1 = (x < (joycentre_x/2));
703       joy[0].stick[0].axis[0].d2 = (x > (joycentre_x*4/3));
704       joy[0].stick[0].axis[1].d1 = (y < (joycentre_y/2));
705       joy[0].stick[0].axis[1].d2 = (y > (joycentre_y*4/3));
706    }
707 
708    if (joystick_flags & JDESC_STICK2) {
709       /* stick 2 position */
710       if ((ABS(x2-joy2_old_x) <= x2/4) && (ABS(y2-joy2_old_y) <= y2/4)) {
711 	 if ((joystick_flags & JOYSTICK_CALIB_TL2) && (joystick_flags & JOYSTICK_CALIB_BR2)) {
712 	    joy[1].stick[0].axis[0].pos = sort_out_analogue(x2, joyx2_min, joyx2_low_margin, joyx2_high_margin, joyx2_max);
713 	    joy[1].stick[0].axis[1].pos = sort_out_analogue(y2, joyy2_min, joyy2_low_margin, joyy2_high_margin, joyy2_max);
714 	 }
715 	 else {
716 	    joy[1].stick[0].axis[0].pos = x2 - joycentre2_x;
717 	    joy[1].stick[0].axis[1].pos = y2 - joycentre2_y;
718 	 }
719 
720 	 joy[1].stick[0].axis[0].d1 = (x2 < (joycentre2_x/2));
721 	 joy[1].stick[0].axis[0].d2 = (x2 > (joycentre2_x*3/2));
722 	 joy[1].stick[0].axis[1].d1 = (y2 < (joycentre2_y/2));
723 	 joy[1].stick[0].axis[1].d2 = (y2 > (joycentre2_y*3/2));
724       }
725 
726       joy[1].button[0].b = ((status & 0x40) == 0);
727       joy[1].button[1].b = ((status & 0x80) == 0);
728    }
729 
730    /* button state */
731    joy[0].button[0].b = ((status & 0x10) == 0);
732    joy[0].button[1].b = ((status & 0x20) == 0);
733 
734    if (joystick_flags & JDESC_4BUTTON) {
735       /* four button state */
736       joy[0].button[2].b = ((status & 0x40) == 0);
737       joy[0].button[3].b = ((status & 0x80) == 0);
738    }
739 
740    if (joystick_flags & JDESC_6BUTTON) {
741       /* six button state */
742       joy[0].button[4].b = (x2 < 128);
743       joy[0].button[5].b = (y2 < 128);
744    }
745    else if (joystick_flags & JDESC_8BUTTON) {
746       /* eight button state */
747       joy[0].button[4].b = (x2 < (joycentre2_x/2));
748       joy[0].button[5].b = (y2 < (joycentre2_y/2));
749       joy[0].button[6].b = (x2 > (joycentre2_x*3/2));
750       joy[0].button[7].b = (y2 > (joycentre2_y*3)/2);
751    }
752 
753    if (joystick_flags & JDESC_Y2_THROTTLE) {
754       /* throttle */
755       if ((joystick_flags & JOYSTICK_CALIB_THRTL_MIN) && (joystick_flags & JOYSTICK_CALIB_THRTL_MAX)) {
756 	 i = (y2 - joy_thr_min) * 255 / (joy_thr_max - joy_thr_min);
757 	 joy[0].stick[2].axis[0].pos = CLAMP(0, i, 255);
758 	 if (joy[0].stick[2].axis[0].pos > 255*2/3)
759 	    joy[0].stick[2].axis[0].d2 = TRUE;
760 	 else
761 	    joy[0].stick[2].axis[0].d2 = FALSE;
762       }
763    }
764 
765    if (joystick_flags & JDESC_FSPRO_HAT) {
766       /* FSPro hat control (accessed via special button values) */
767       joy[0].stick[1].axis[0].pos = 0;
768       joy[0].stick[1].axis[1].pos = 0;
769       joy[0].stick[1].axis[0].d1 = joy[0].stick[1].axis[0].d2 = 0;
770       joy[0].stick[1].axis[1].d1 = joy[0].stick[1].axis[1].d2 = 0;
771 
772       if ((status & 0x30) == 0) {
773 	 joy[0].button[0].b = FALSE;
774 	 joy[0].button[1].b = FALSE;
775 	 joy[0].button[2].b = FALSE;
776 	 joy[0].button[3].b = FALSE;
777 
778 	 switch (status & 0xC0) {
779 
780 	    case 0x00:
781 	       /* up */
782 	       joy[0].stick[1].axis[1].pos = -128;
783 	       joy[0].stick[1].axis[1].d1 = TRUE;
784 	       break;
785 
786 	    case 0x40:
787 	       /* right */
788 	       joy[0].stick[1].axis[0].pos = 128;
789 	       joy[0].stick[1].axis[0].d2 = TRUE;
790 	       break;
791 
792 	    case 0x80:
793 	       /* down */
794 	       joy[0].stick[1].axis[1].pos = 128;
795 	       joy[0].stick[1].axis[1].d2 = TRUE;
796 	       break;
797 
798 	    case 0xC0:
799 	       /* left */
800 	       joy[0].stick[1].axis[0].pos = -128;
801 	       joy[0].stick[1].axis[0].d1 = TRUE;
802 	       break;
803 	 }
804       }
805    }
806 
807    if (joystick_flags & JDESC_Y2_HAT) {
808       /* Wingman Extreme hat control (accessed via the y2 pot) */
809       joy[0].stick[1].axis[0].pos = 0;
810       joy[0].stick[1].axis[1].pos = 0;
811       joy[0].stick[1].axis[0].d1 = joy[0].stick[1].axis[0].d2 = 0;
812       joy[0].stick[1].axis[1].d1 = joy[0].stick[1].axis[1].d2 = 0;
813 
814       if ((joystick_flags & JOYSTICK_CALIB_HAT) == JOYSTICK_CALIB_HAT) {
815 	 if (y2 >= joy_hat_threshold[0]) {
816 	    /* centre */
817 	 }
818 	 else if (y2 >= joy_hat_threshold[1]) {
819 	    /* left */
820 	    joy[0].stick[1].axis[0].pos = -128;
821 	    joy[0].stick[1].axis[0].d1 = TRUE;
822 	 }
823 	 else if (y2 >= joy_hat_threshold[2]) {
824 	    /* down */
825 	    joy[0].stick[1].axis[1].pos = 128;
826 	    joy[0].stick[1].axis[1].d2 = TRUE;
827 	 }
828 	 else if (y2 >= joy_hat_threshold[3]) {
829 	    /* right */
830 	    joy[0].stick[1].axis[0].pos = 128;
831 	    joy[0].stick[1].axis[0].d2 = TRUE;
832 	 }
833 	 else {
834 	    /* up */
835 	    joy[0].stick[1].axis[1].pos = -128;
836 	    joy[0].stick[1].axis[1].d1 = TRUE;
837 	 }
838       }
839    }
840 
841    joy_old_x = x;
842    joy_old_y = y;
843 
844    joy2_old_x = x2;
845    joy2_old_y = y2;
846 
847    return 0;
848 }
849 
850 
851 
852 /* poll:
853  *  Polls the joystick to read the axis position. Returns raw position
854  *  values, zero for success, non-zero if no joystick found. This has
855  *  to come after the routines that call it, to make sure it will never
856  *  be inlined (that could shift alignment and upset the timing values).
857  */
poll(int * x,int * y,int * x2,int * y2,int poll_mask)858 static int poll(int *x, int *y, int *x2, int *y2, int poll_mask)
859 {
860    int c, d;
861 
862    *x = *y = *x2 = *y2 = 0;
863 
864    _enter_critical();
865 
866    outportb(0x201, 0);
867 
868    for (c=0; c<100000; c++) {
869       d = inportb(0x201);
870 
871       if (d & MASK_1X)
872 	 (*x)++;
873 
874       if (d & MASK_1Y)
875 	 (*y)++;
876 
877       if (d & MASK_2X)
878 	 (*x2)++;
879 
880       if (d & MASK_2Y)
881 	 (*y2)++;
882 
883       if (!(d & poll_mask))
884 	 break;
885    }
886 
887    _exit_critical();
888 
889    if (((poll_mask & MASK_1X) && (*x >= 100000)) ||
890        ((poll_mask & MASK_1Y) && (*y >= 100000)) ||
891        ((poll_mask & MASK_2X) && (*x2 >= 100000)) ||
892        ((poll_mask & MASK_2Y) && (*y2 >= 100000)))
893       return -1;
894 
895    return 0;
896 }
897 
898 
899 
900 /* joy_save_data:
901  *  Writes calibration data into the config file.
902  */
joy_save_data(void)903 static int joy_save_data(void)
904 {
905    char tmp1[128], tmp2[128];
906    char *j = uconvert_ascii("joystick", tmp1);
907 
908    set_config_int(j, uconvert_ascii("joystick_flags", tmp2),    joystick_flags);
909 
910    set_config_int(j, uconvert_ascii("joycentre_x", tmp2),       joycentre_x);
911    set_config_int(j, uconvert_ascii("joycentre_y", tmp2),       joycentre_y);
912    set_config_int(j, uconvert_ascii("joyx_min", tmp2),          joyx_min);
913    set_config_int(j, uconvert_ascii("joyx_low_margin", tmp2),   joyx_low_margin);
914    set_config_int(j, uconvert_ascii("joyx_high_margin", tmp2),  joyx_high_margin);
915    set_config_int(j, uconvert_ascii("joyx_max", tmp2),          joyx_max);
916    set_config_int(j, uconvert_ascii("joyy_min", tmp2),          joyy_min);
917    set_config_int(j, uconvert_ascii("joyy_low_margin", tmp2),   joyy_low_margin);
918    set_config_int(j, uconvert_ascii("joyy_high_margin", tmp2),  joyy_high_margin);
919    set_config_int(j, uconvert_ascii("joyy_max", tmp2),          joyy_max);
920 
921    set_config_int(j, uconvert_ascii("joycentre2_x", tmp2),      joycentre2_x);
922    set_config_int(j, uconvert_ascii("joycentre2_y", tmp2),      joycentre2_y);
923    set_config_int(j, uconvert_ascii("joyx2_min", tmp2),         joyx2_min);
924    set_config_int(j, uconvert_ascii("joyx2_low_margin", tmp2),  joyx2_low_margin);
925    set_config_int(j, uconvert_ascii("joyx2_high_margin", tmp2), joyx2_high_margin);
926    set_config_int(j, uconvert_ascii("joyx2_max", tmp2),         joyx2_max);
927    set_config_int(j, uconvert_ascii("joyy2_min", tmp2),         joyy2_min);
928    set_config_int(j, uconvert_ascii("joyy2_low_margin", tmp2),  joyy2_low_margin);
929    set_config_int(j, uconvert_ascii("joyy2_high_margin", tmp2), joyy2_high_margin);
930    set_config_int(j, uconvert_ascii("joyy2_max", tmp2),         joyy2_max);
931 
932    set_config_int(j, uconvert_ascii("joythr_min", tmp2),        joy_thr_min);
933    set_config_int(j, uconvert_ascii("joythr_max", tmp2),        joy_thr_max);
934 
935    set_config_int(j, uconvert_ascii("joyhat_0", tmp2),          joy_hat_threshold[0]);
936    set_config_int(j, uconvert_ascii("joyhat_1", tmp2),          joy_hat_threshold[1]);
937    set_config_int(j, uconvert_ascii("joyhat_2", tmp2),          joy_hat_threshold[2]);
938    set_config_int(j, uconvert_ascii("joyhat_3", tmp2),          joy_hat_threshold[3]);
939 
940    return 0;
941 }
942 
943 
944 
945 /* joy_load_data:
946  *  Loads calibration data from the config file.
947  */
joy_load_data(void)948 static int joy_load_data(void)
949 {
950    char tmp1[128], tmp2[128];
951    char *j = uconvert_ascii("joystick", tmp1);
952 
953    joystick_flags    = get_config_int(j, uconvert_ascii("joystick_flags", tmp2), joystick_flags);
954 
955    joycentre_x       = get_config_int(j, uconvert_ascii("joycentre_x", tmp2), joycentre_x);
956    joycentre_y       = get_config_int(j, uconvert_ascii("joycentre_y", tmp2), joycentre_y);
957    joyx_min          = get_config_int(j, uconvert_ascii("joyx_min", tmp2), joyx_min);
958    joyx_low_margin   = get_config_int(j, uconvert_ascii("joyx_low_margin", tmp2), joyx_low_margin);
959    joyx_high_margin  = get_config_int(j, uconvert_ascii("joyx_high_margin", tmp2), joyx_high_margin);
960    joyx_max          = get_config_int(j, uconvert_ascii("joyx_max", tmp2), joyx_max);
961    joyy_min          = get_config_int(j, uconvert_ascii("joyy_min", tmp2), joyy_min);
962    joyy_low_margin   = get_config_int(j, uconvert_ascii("joyy_low_margin", tmp2), joyy_low_margin);
963    joyy_high_margin  = get_config_int(j, uconvert_ascii("joyy_high_margin", tmp2), joyy_high_margin);
964    joyy_max          = get_config_int(j, uconvert_ascii("joyy_max", tmp2), joyy_max);
965 
966    joycentre2_x      = get_config_int(j, uconvert_ascii("joycentre2_x", tmp2), joycentre2_x);
967    joycentre2_y      = get_config_int(j, uconvert_ascii("joycentre2_y", tmp2), joycentre2_y);
968    joyx2_min         = get_config_int(j, uconvert_ascii("joyx2_min", tmp2), joyx2_min);
969    joyx2_low_margin  = get_config_int(j, uconvert_ascii("joyx_low2_margin", tmp2), joyx2_low_margin);
970    joyx2_high_margin = get_config_int(j, uconvert_ascii("joyx_high2_margin", tmp2), joyx2_high_margin);
971    joyx2_max         = get_config_int(j, uconvert_ascii("joyx2_max", tmp2), joyx2_max);
972    joyy2_min         = get_config_int(j, uconvert_ascii("joyy2_min", tmp2), joyy2_min);
973    joyy2_low_margin  = get_config_int(j, uconvert_ascii("joyy2_low_margin", tmp2), joyy2_low_margin);
974    joyy2_high_margin = get_config_int(j, uconvert_ascii("joyy2_high_margin", tmp2), joyy2_high_margin);
975    joyy2_max         = get_config_int(j, uconvert_ascii("joyy2_max", tmp2), joyy2_max);
976 
977    joy_thr_min       = get_config_int(j, uconvert_ascii("joythr_min", tmp2), joy_thr_min);
978    joy_thr_max       = get_config_int(j, uconvert_ascii("joythr_max", tmp2), joy_thr_max);
979 
980    joy_hat_threshold[0] = get_config_int(j, uconvert_ascii("joyhat_0", tmp2), joy_hat_threshold[0]);
981    joy_hat_threshold[1] = get_config_int(j, uconvert_ascii("joyhat_1", tmp2), joy_hat_threshold[1]);
982    joy_hat_threshold[2] = get_config_int(j, uconvert_ascii("joyhat_2", tmp2), joy_hat_threshold[2]);
983    joy_hat_threshold[3] = get_config_int(j, uconvert_ascii("joyhat_3", tmp2), joy_hat_threshold[3]);
984 
985    joy_old_x = joy_old_y = 0;
986 
987    recalc_calibration_flags();
988 
989    return 0;
990 }
991 
992 
993 
994 /* next_calib_action:
995  *  Returns a flag indicating the next thing that needs to be calibrated.
996  */
next_calib_action(int stick)997 static int next_calib_action(int stick)
998 {
999    if (stick == 0) {
1000       /* stick 1 analogue input? */
1001       if (!(joystick_flags & JOYSTICK_CALIB_TL1))
1002 	 return JOYSTICK_CALIB_TL1;
1003 
1004       if (!(joystick_flags & JOYSTICK_CALIB_BR1))
1005 	 return JOYSTICK_CALIB_BR1;
1006 
1007       /* FSPro throttle input? */
1008       if (joystick_flags & JDESC_Y2_THROTTLE) {
1009 	 if (!(joystick_flags & JOYSTICK_CALIB_THRTL_MIN))
1010 	    return JOYSTICK_CALIB_THRTL_MIN;
1011 
1012 	 if (!(joystick_flags & JOYSTICK_CALIB_THRTL_MAX))
1013 	    return JOYSTICK_CALIB_THRTL_MAX;
1014       }
1015 
1016       /* Wingman Extreme hat input? */
1017       if (joystick_flags & JDESC_Y2_HAT) {
1018 	 if (!(joystick_flags & JOYSTICK_CALIB_HAT_CENTRE))
1019 	    return JOYSTICK_CALIB_HAT_CENTRE;
1020 
1021 	 if (!(joystick_flags & JOYSTICK_CALIB_HAT_LEFT))
1022 	    return JOYSTICK_CALIB_HAT_LEFT;
1023 
1024 	 if (!(joystick_flags & JOYSTICK_CALIB_HAT_DOWN))
1025 	    return JOYSTICK_CALIB_HAT_DOWN;
1026 
1027 	 if (!(joystick_flags & JOYSTICK_CALIB_HAT_RIGHT))
1028 	    return JOYSTICK_CALIB_HAT_RIGHT;
1029 
1030 	 if (!(joystick_flags & JOYSTICK_CALIB_HAT_UP))
1031 	    return JOYSTICK_CALIB_HAT_UP;
1032       }
1033    }
1034    else if (stick == 1) {
1035       if (joystick_flags & JDESC_STICK2) {
1036 	 /* stick 2 analogue input? */
1037 	 if (!(joystick_flags & JOYSTICK_CALIB_TL2))
1038 	    return JOYSTICK_CALIB_TL2;
1039 
1040 	 if (!(joystick_flags & JOYSTICK_CALIB_BR2))
1041 	    return JOYSTICK_CALIB_BR2;
1042       }
1043    }
1044 
1045    return 0;
1046 }
1047 
1048 
1049 
1050 /* joy_calibrate_name:
1051  *  Returns the name of the next calibration operation.
1052  */
joy_calibrate_name(int n)1053 static AL_CONST char *joy_calibrate_name(int n)
1054 {
1055    switch (next_calib_action(n)) {
1056 
1057       case JOYSTICK_CALIB_TL1:         return get_config_text("Move stick to the top left");         break;
1058       case JOYSTICK_CALIB_BR1:         return get_config_text("Move stick to the bottom right");     break;
1059       case JOYSTICK_CALIB_TL2:         return get_config_text("Move stick 2 to the top left");       break;
1060       case JOYSTICK_CALIB_BR2:         return get_config_text("Move stick 2 to the bottom right");   break;
1061       case JOYSTICK_CALIB_THRTL_MIN:   return get_config_text("Set throttle to minimum");            break;
1062       case JOYSTICK_CALIB_THRTL_MAX:   return get_config_text("Set throttle to maximum");            break;
1063       case JOYSTICK_CALIB_HAT_CENTRE:  return get_config_text("Centre the hat");                     break;
1064       case JOYSTICK_CALIB_HAT_LEFT:    return get_config_text("Move the hat left");                  break;
1065       case JOYSTICK_CALIB_HAT_DOWN:    return get_config_text("Move the hat down");                  break;
1066       case JOYSTICK_CALIB_HAT_RIGHT:   return get_config_text("Move the hat right");                 break;
1067       case JOYSTICK_CALIB_HAT_UP:      return get_config_text("Move the hat up");                    break;
1068    }
1069 
1070    return NULL;
1071 }
1072 
1073 
1074 
1075 /* joy_calibrate:
1076  *  Performs the next calibration operation.
1077  */
joy_calibrate(int n)1078 static int joy_calibrate(int n)
1079 {
1080    switch (next_calib_action(n)) {
1081 
1082       case JOYSTICK_CALIB_TL1:
1083 	 return calibrate_corner(0, 0);
1084 	 break;
1085 
1086       case JOYSTICK_CALIB_BR1:
1087 	 return calibrate_corner(0, 1);
1088 	 break;
1089 
1090       case JOYSTICK_CALIB_TL2:
1091 	 return calibrate_corner(1, 0);
1092 	 break;
1093 
1094       case JOYSTICK_CALIB_BR2:
1095 	 return calibrate_corner(1, 1);
1096 	 break;
1097 
1098       case JOYSTICK_CALIB_THRTL_MIN:
1099 	 return calibrate_joystick_throttle_min();
1100 	 break;
1101 
1102       case JOYSTICK_CALIB_THRTL_MAX:
1103 	 return calibrate_joystick_throttle_max();
1104 	 break;
1105 
1106       case JOYSTICK_CALIB_HAT_CENTRE:
1107 	 return calibrate_joystick_hat(JOY_HAT_CENTRE);
1108 	 break;
1109 
1110       case JOYSTICK_CALIB_HAT_LEFT:
1111 	 return calibrate_joystick_hat(JOY_HAT_LEFT);
1112 	 break;
1113 
1114       case JOYSTICK_CALIB_HAT_DOWN:
1115 	 return calibrate_joystick_hat(JOY_HAT_DOWN);
1116 	 break;
1117 
1118       case JOYSTICK_CALIB_HAT_RIGHT:
1119 	 return calibrate_joystick_hat(JOY_HAT_RIGHT);
1120 	 break;
1121 
1122       case JOYSTICK_CALIB_HAT_UP:
1123 	 return calibrate_joystick_hat(JOY_HAT_UP);
1124 	 break;
1125    }
1126 
1127    return -1;
1128 }
1129 
1130 
1131