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