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