]> icculus.org git repositories - taylor/freespace2.git/blob - src/io/joy.cpp
fix popup condition testing
[taylor/freespace2.git] / src / io / joy.cpp
1 /*
2  * Copyright (C) Volition, Inc. 1999.  All rights reserved.
3  *
4  * All source code herein is the property of Volition, Inc. You may not sell
5  * or otherwise commercially exploit the source or things you created based on
6  * the source.
7  */
8
9 #include "pstypes.h"
10 #include "joy.h"
11 #include "fix.h"
12 #include "key.h"
13 #include "timer.h"
14 #include "osregistry.h"
15 #include "joy_ff.h"
16 #include "osapi.h"
17
18
19 static int Joy_inited = 0;
20 int Dead_zone_size = 10;
21 int Joy_sensitivity = 9;
22
23 static int Joy_last_x_reading = 0;
24 static int Joy_last_y_reading = 0;
25
26 typedef struct joy_button_info {
27         int     actual_state;           // Set if the button is physically down
28         int     state;                          // Set when the button goes from up to down, cleared on down to up.  Different than actual_state after a flush.
29         int     down_count;
30         int     up_count;
31         int     down_time;
32         uint    last_down_check;        // timestamp in milliseconds of last
33 } joy_button_info;
34
35 static Joy_info joystick;
36
37 SDL_Joystick *sdljoy;
38 static SDL_JoystickID joy_id = -1;
39
40 joy_button_info joy_buttons[JOY_TOTAL_BUTTONS];
41
42
43 int joystick_get_id()
44 {
45         return joy_id;
46 }
47
48 void joy_close()
49 {
50         if (!Joy_inited)
51                 return;
52
53         joy_ff_shutdown();
54
55         Joy_inited = 0;
56         joy_id = -1;
57
58         if (SDL_JoystickGetAttached(sdljoy)) {
59                 SDL_JoystickClose(sdljoy);
60         }
61
62         sdljoy = NULL;
63
64         SDL_QuitSubSystem(SDL_INIT_JOYSTICK);
65 }
66
67 int joy_down(int btn)
68 {
69         int tmp;
70
71         if ( !Joy_inited ) {
72                 return 0;
73         }
74
75         if ( (btn < 0) || (btn >= JOY_TOTAL_BUTTONS)) {
76                 return 0;
77         }
78
79         tmp = joy_buttons[btn].state;
80
81         return tmp;
82 }
83
84 int joy_down_count(int btn, int reset_count)
85 {
86         int tmp;
87
88         if ( !Joy_inited ) {
89                 return 0;
90         }
91
92         if ( (btn < 0) || (btn >= JOY_TOTAL_BUTTONS)) {
93                 return 0;
94         }
95
96         tmp = joy_buttons[btn].down_count;
97         if ( reset_count ) {
98                 joy_buttons[btn].down_count = 0;
99         }
100
101         return tmp;
102 }
103
104 float joy_down_time(int btn)
105 {
106         float                           rval;
107         unsigned int    now, delta;
108         joy_button_info         *bi;
109
110         if ( !Joy_inited ) {
111                 return 0.0f;
112         }
113
114         if ( (btn < 0) || (btn >= JOY_TOTAL_BUTTONS)) {
115                 return 0.0f;
116         }
117
118         bi = &joy_buttons[btn];
119
120         now = timer_get_milliseconds();
121         delta = now - bi->last_down_check;
122
123         if ( (now - bi->last_down_check) > 0)
124                 rval = i2fl((now - bi->down_time)) / delta;
125         else
126                 rval = 0.0f;
127
128         bi->down_time = 0;
129         bi->last_down_check = now;
130
131         if (rval < 0)
132                 rval = 0.0f;
133         if (rval > 1)
134                 rval = 1.0f;
135
136         return rval;
137 }
138
139 void joy_mark_button(int btn, int state)
140 {
141         int i;
142         joy_button_info *bi;
143
144         if ( !Joy_inited ) {
145                 return;
146         }
147
148         if ( (btn < 0) || (btn >= JOY_TOTAL_BUTTONS)) {
149                 return;
150         }
151
152         bi = &joy_buttons[btn];
153
154         if (state) {
155                 // button pressed
156                 bi->down_count++;
157
158                 bi->state = 1;
159                 bi->down_time = timer_get_milliseconds();
160
161                 // toggle off other positions if hat
162                 if (btn >= JOY_HATBACK) {
163                         for (i = JOY_HATBACK; i < (JOY_HATBACK+JOY_NUM_HAT_POS); i++) {
164                                 if (btn != i) {
165                                         joy_buttons[i].state = 0;
166                                 }
167                         }
168                 }
169         } else {
170                 // button released
171         //      bi->up_count++;
172
173                 bi->state = 0;
174
175                 // special hat handling - make sure all hat pos are off
176                 if (btn == JOY_HATBACK) {
177                         for (i = JOY_HATBACK; i < (JOY_HATBACK+JOY_NUM_HAT_POS); i++) {
178                                 joy_buttons[i].state = 0;
179                         }
180                 }
181         }
182 }
183
184 void joy_flush()
185 {
186         int                     i;
187         joy_button_info *bi;
188
189         if ( !Joy_inited ) {
190                 return;
191         }
192
193         for ( i = 0; i < JOY_TOTAL_BUTTONS; i++) {
194                 bi = &joy_buttons[i];
195                 bi->state               = 0;
196                 bi->down_count  = 0;
197                 bi->up_count    = 0;
198                 bi->down_time   = 0;
199                 bi->last_down_check = timer_get_milliseconds();
200         }
201 }
202
203 int joy_get_unscaled_reading(int axn)
204 {
205         int rng;
206
207         if ( !Joy_inited ) {
208                 return 0;
209         }
210
211         if (axn >= joystick.num_axes) {
212                 return 0;
213         }
214
215         int raw = joystick.axis_current[axn];
216
217         rng = joystick.axis_max[axn] - joystick.axis_min[axn];
218         raw -= joystick.axis_min[axn];  // adjust for linear range starting at 0
219
220         // cap at limits
221         if (raw < 0)
222                 raw = 0;
223         if (raw > rng)
224                 raw = rng;
225
226         return (int) ((unsigned int) raw * (unsigned int) F1_0 / (unsigned int) rng);  // convert to 0 - F1_0 range.
227 }
228
229 // --------------------------------------------------------------
230 //      joy_get_scaled_reading()
231 //
232 //      input:  raw     =>      the raw value for an axis position
233 //                              axn     =>      axis number, numbered starting at 0
234 //
235 // return:      joy_get_scaled_reading will return a value that represents
236 //                              the joystick pos from -1 to +1 for the specified axis number 'axn', and
237 //                              the raw value 'raw'
238 //
239 int joy_get_scaled_reading(int axn)
240 {
241         int x, d, dead_zone, rng, raw;
242         float percent, sensitivity_percent, non_sensitivity_percent;
243
244         if ( !Joy_inited ) {
245                 return 0;
246         }
247
248         if (axn >= joystick.num_axes) {
249                 return 0;
250         }
251
252         raw = joystick.axis_current[axn] - joystick.axis_center[axn];
253
254         dead_zone = (joystick.axis_max[axn] - joystick.axis_min[axn]) * Dead_zone_size / 100;
255
256         if (raw < -dead_zone) {
257                 rng = joystick.axis_center[axn] - joystick.axis_min[axn] - dead_zone;
258                 d = -raw - dead_zone;
259
260         } else if (raw > dead_zone) {
261                 rng = joystick.axis_max[axn] - joystick.axis_center[axn] - dead_zone;
262                 d = raw - dead_zone;
263
264         } else {
265                 return 0;
266         }
267
268         if (d > rng)
269                 d = rng;
270
271         SDL_assert(Joy_sensitivity >= 0 && Joy_sensitivity <= 9);
272
273         // compute percentages as a range between 0 and 1
274         sensitivity_percent = (float) Joy_sensitivity / 9.0f;
275         non_sensitivity_percent = (float) (9 - Joy_sensitivity) / 9.0f;
276
277         // find percent of max axis is at
278         percent = (float) d / (float) rng;
279
280         // work sensitivity on axis value
281         percent = (percent * sensitivity_percent + percent * percent * percent * percent * percent * non_sensitivity_percent);
282
283         x = (int) ((float) F1_0 * percent);
284
285         //nprintf(("AI", "d=%6i, sens=%3i, percent=%6.3f, val=%6i, ratio=%6.3f\n", d, Joy_sensitivity, percent, (raw<0) ? -x : x, (float) d/x));
286
287         if (raw < 0) {
288                 return -x;
289         }
290
291         return x;
292 }
293
294 // --------------------------------------------------------------
295 //      joy_get_pos()
296 //
297 //      input:  x               =>              OUTPUT PARAMETER: x-axis position of stick (-1 to 1)
298 //                              y               =>              OUTPUT PARAMETER: y-axis position of stick (-1 to 1)
299 //                              z               =>              OUTPUT PARAMETER: z-axis (throttle) position of stick (-1 to 1)
300 //                              r               =>              OUTPUT PARAMETER: rudder position of stick (-1 to 1)
301 //
302 //      return: success => 1
303 //                              failure => 0
304 //
305 int joy_get_pos(int *x, int *y, int *z, int *rx)
306 {
307         if (x) *x = 0;
308         if (y) *y = 0;
309         if (z) *z = 0;
310         if (rx) *rx = 0;
311
312         if ( !Joy_inited ) {
313                 return 0;
314         }
315
316         //      joy_get_scaled_reading will return a value represents the joystick
317         //      pos from -1 to +1
318         if (x && joystick.num_axes > 0) {
319                 *x = joy_get_scaled_reading(0);
320                 Joy_last_x_reading = *x;
321         }
322
323         if (y && joystick.num_axes > 1) {
324                 *y = joy_get_scaled_reading(1);
325                 Joy_last_y_reading = *y;
326         }
327
328         if (z && joystick.num_axes > 2) {
329                 *z = joy_get_unscaled_reading(2);
330         }
331
332         if (rx && joystick.num_axes > 3) {
333                 *rx = joy_get_scaled_reading(3);
334         }
335
336         return 1;
337 }
338
339 int joy_init()
340 {
341         int i, num_sticks;
342         const char *ptr = NULL;
343         int Cur_joystick;
344
345         if (Joy_inited) {
346                 return 0;
347         }
348
349         mprintf(("Initializing Joystick...\n"));
350
351         if (SDL_InitSubSystem(SDL_INIT_JOYSTICK) < 0) {
352                 mprintf(("  Could not initialize joystick subsystem\n\n"));
353                 return 0;
354         }
355
356         num_sticks = SDL_NumJoysticks();
357
358         if (num_sticks < 1) {
359                 mprintf(("  No joysticks found\n\n"));
360                 return 0;
361         }
362
363         Cur_joystick = 0;
364
365         ptr = os_config_read_string("Controls", "CurrentJoystick", NULL);
366
367         if ( ptr && SDL_strlen(ptr) ) {
368                 for (i = 0; i < num_sticks; i++) {
369                         const char *jname = SDL_JoystickNameForIndex(i);
370
371                         if ( jname && !SDL_strcasecmp(ptr, jname) ) {
372                                 Cur_joystick = i;
373                                 break;
374                         }
375                 }
376         }
377
378         sdljoy = SDL_JoystickOpen(Cur_joystick);
379
380         if (sdljoy == NULL) {
381                 mprintf(("  Unable to init joystick %d (%s)\n\n", Cur_joystick, SDL_JoystickNameForIndex(Cur_joystick)));
382                 return 0;
383         }
384
385         mprintf(("  Name         : %s\n", SDL_JoystickName(sdljoy)));
386         mprintf(("  Axes         : %d\n", SDL_JoystickNumAxes(sdljoy)));
387         mprintf(("  Buttons      : %d\n", SDL_JoystickNumButtons(sdljoy)));
388         mprintf(("  Hats         : %d\n", SDL_JoystickNumHats(sdljoy)));
389         mprintf(("  Haptic       : %s\n", SDL_JoystickIsHaptic(sdljoy) ? "Yes" : "No"));
390
391         joy_ff_init();
392
393         mprintf(("\n"));
394
395         Joy_inited = 1;
396
397         joy_id = SDL_JoystickInstanceID(sdljoy);
398
399         joystick.num_axes = SDL_JoystickNumAxes(sdljoy);
400
401         joy_flush();
402
403         // Fake a calibration
404         joy_set_cen();
405
406         for (i = 0; i < JOY_NUM_AXES; i++) {
407                 joystick.axis_min[i] = 0;
408                 joystick.axis_max[i] = 65536;
409                 joystick.axis_current[i] = joystick.axis_center[i];
410         }
411
412         return num_sticks;
413 }
414
415 void joy_reinit(int with_index)
416 {
417         int i, num_sticks;
418         const char *ptr = NULL;
419         int Cur_joystick;
420
421         if ( !Joy_inited ) {
422                 joy_init();
423                 return;
424         }
425
426         // close out what we have already opened...
427         joy_ff_shutdown();
428
429         joy_id = -1;
430
431         if (SDL_JoystickGetAttached(sdljoy)) {
432                 SDL_JoystickClose(sdljoy);
433         }
434
435         sdljoy = NULL;
436
437         // attempt to get a new joystick to use...
438         mprintf(("Re-Initializing Joystick...\n"));
439
440         num_sticks = SDL_NumJoysticks();
441
442         if (num_sticks < 1) {
443                 mprintf(("  No joysticks found\n\n"));
444                 return;
445         }
446
447         if ( (with_index >= 0) && (with_index < num_sticks) ) {
448                 Cur_joystick = with_index;
449         } else {
450                 Cur_joystick = 0;
451
452                 ptr = os_config_read_string("Controls", "CurrentJoystick", NULL);
453
454                 if ( ptr && SDL_strlen(ptr) ) {
455                         for (i = 0; i < num_sticks; i++) {
456                                 const char *jname = SDL_JoystickNameForIndex(i);
457
458                                 if ( jname && !SDL_strcasecmp(ptr, jname) ) {
459                                         Cur_joystick = i;
460                                         break;
461                                 }
462                         }
463                 }
464         }
465
466         sdljoy = SDL_JoystickOpen(Cur_joystick);
467
468         if (sdljoy == NULL) {
469                 mprintf(("  Unable to init joystick %d (%s)\n\n", Cur_joystick, SDL_JoystickNameForIndex(Cur_joystick)));
470                 return;
471         }
472
473         mprintf(("  Name         : %s\n", SDL_JoystickName(sdljoy)));
474         mprintf(("  Axes         : %d\n", SDL_JoystickNumAxes(sdljoy)));
475         mprintf(("  Buttons      : %d\n", SDL_JoystickNumButtons(sdljoy)));
476         mprintf(("  Hats         : %d\n", SDL_JoystickNumHats(sdljoy)));
477         mprintf(("  Haptic       : %s\n", SDL_JoystickIsHaptic(sdljoy) ? "Yes" : "No"));
478
479         joy_ff_init();
480
481         mprintf(("\n"));
482
483         joy_id = SDL_JoystickInstanceID(sdljoy);
484
485         joystick.num_axes = SDL_JoystickNumAxes(sdljoy);
486
487         joy_flush();
488
489         // Fake a calibration
490         joy_set_cen();
491
492         for (i = 0; i < JOY_NUM_AXES; i++) {
493                 joystick.axis_min[i] = 0;
494                 joystick.axis_max[i] = 65536;
495                 joystick.axis_current[i] = joystick.axis_center[i];
496         }
497 }
498
499 void joy_set_cen()
500 {
501         if ( !Joy_inited ) {
502                 return;
503         }
504
505         for (int i = 0; i < JOY_NUM_AXES; i++) {
506                 if (i < joystick.num_axes) {
507                         joystick.axis_center[i] = SDL_JoystickGetAxis(sdljoy, i) + 32768;
508                 } else {
509                         joystick.axis_center[i] = 32768;
510                 }
511         }
512 }
513
514 int joystick_read_raw_axis(int num_axes, int *axis)
515 {
516         int i;
517
518         if ( !Joy_inited ) {
519                 return 0;
520         }
521
522         for (i = 0; i < num_axes; i++) {
523                 if (i < joystick.num_axes) {
524                         axis[i] = joystick.axis_current[i];
525                 } else {
526                         axis[i] = 32768;
527                 }
528         }
529
530         return 1;
531 }
532
533 bool joy_axis_valid(int axis)
534 {
535         return (axis < joystick.num_axes);
536 }
537
538 void joystick_update_axis(int axis, int value)
539 {
540         if (axis < JOY_NUM_AXES) {
541                 joystick.axis_current[axis] = value + 32768;
542         }
543 }