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