1 /*
2  * Triplane Classic - a side-scrolling dogfighting game.
3  * Copyright (C) 1996,1997,2009  Dodekaedron Software Creations Oy
4  *
5  * This program is free software: you can redistribute it and/or modify
6  * it under the terms of the GNU General Public License as published by
7  * the Free Software Foundation, either version 3 of the License, or
8  * (at your option) any later version.
9  *
10  * This program is distributed in the hope that it will be useful,
11  * but WITHOUT ANY WARRANTY; without even the implied warranty of
12  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
13  * GNU General Public License for more details.
14  *
15  * You should have received a copy of the GNU General Public License
16  * along with this program.  If not, see <http://www.gnu.org/licenses/>.
17  *
18  * tjt@users.sourceforge.net
19  */
20 
21 #include <assert.h>
22 #include <stdio.h>
23 #include <SDL.h>
24 #include <string.h>
25 #include "sdl_compat.h"
26 #include "io/joystick.h"
27 #include "settings.h"
28 #include "util/wutil.h"
29 
30 /*
31  * min/max value given by joystick axis is multiplied by this to give
32  * the threshold for when a direction is active
33  */
34 #define JOYSTICK_THRESHOLD_MULTIPLIER 0.8
35 
36 joystick_configuration joystick_config[2];
37 
38 SDL_Joystick *joydev[2] = { NULL, NULL };
39 
40 /**
41  * Initializes joystick configuration.
42  * @return Bitwise or of JOY1X, JOY1Y, JOY2X and JOY2Y as
43  * appropriate. 1 bit means that a device providing the axis is available
44  */
init_joysticks(void)45 short init_joysticks(void) {
46     /* set default config, used if the configuration file does not exist */
47     joystick_config[0].up.type = 2;
48     joystick_config[0].up.n = 1;
49     joystick_config[0].up.threshold = (Sint16) (32767 * JOYSTICK_THRESHOLD_MULTIPLIER);
50     joystick_config[0].down.type = 2;
51     joystick_config[0].down.n = 1;
52     joystick_config[0].down.threshold = (Sint16) (-32767 * JOYSTICK_THRESHOLD_MULTIPLIER);
53     joystick_config[0].power.type = 2;
54     joystick_config[0].power.n = 0;
55     joystick_config[0].power.threshold = (Sint16) (32767 * JOYSTICK_THRESHOLD_MULTIPLIER);
56     joystick_config[0].brake.type = 2;
57     joystick_config[0].brake.n = 0;
58     joystick_config[0].brake.threshold = (Sint16) (-32767 * JOYSTICK_THRESHOLD_MULTIPLIER);
59     joystick_config[0].guns.type = 1;
60     joystick_config[0].guns.n = 0;
61     joystick_config[0].guns.threshold = 0;
62     joystick_config[0].bombs.type = 1;
63     joystick_config[0].bombs.n = 1;
64     joystick_config[0].bombs.threshold = 0;
65     joystick_config[0].roll.type = 0;
66     joystick_config[0].roll.n = 0;
67     joystick_config[0].roll.threshold = 0;
68     /*
69      * use these for noautoroll:
70      * joystick_config[0].roll.type = 1;
71      * joystick_config[0].roll.n = 2;
72      * joystick_config[0].roll.threshold = 0;
73      */
74 
75     memcpy(&joystick_config[1], &joystick_config[0], sizeof(joystick_configuration));
76 
77     if (SDL_NumJoysticks() >= 2)
78         return JOY1X | JOY1Y | JOY2X | JOY2Y;
79     else if (SDL_NumJoysticks() == 1)
80         return JOY1X | JOY1Y;
81     else
82         return 0;
83 }
84 
85 /**
86  * Loads saved joystic presence and calibration data.
87  * @return 1 if succeeded in loading something
88  */
load_joysticks_data(const char * filename)89 int load_joysticks_data(const char *filename) {
90     FILE *fp;
91 
92     fp = settings_open(filename, "rb");
93 
94     if (fp != NULL) {
95         fread(&joystick_config, sizeof(joystick_configuration), 2, fp);
96         fclose(fp);
97         return 1;
98     }
99 
100     return 0;
101 }
102 
103 /**
104  * Saves joystic precense and calibration data.
105  */
106 
save_joysticks_data(const char * filename)107 void save_joysticks_data(const char *filename) {
108     FILE *fp = settings_open(filename, "wb");
109     fwrite(&joystick_config, sizeof(joystick_configuration), 2, fp);
110     fclose(fp);
111 }
112 
113 /**
114  * Open and close joystick devices according to the arguments.
115  * @param joy1 = 1 if joystick 1 should be opened
116  * @param joy2 = 1 if joystick 2 should be opened
117  */
open_close_joysticks(int joy1,int joy2)118 void open_close_joysticks(int joy1, int joy2) {
119     if (SDL_NumJoysticks() >= 1) {
120         if (!joy1 && SDL_JoystickOpened(0)) {
121             SDL_JoystickClose(joydev[0]);
122             joydev[0] = NULL;
123         }
124         if (joy1 && !SDL_JoystickOpened(0)) {
125             joydev[0] = SDL_JoystickOpen(0);
126         }
127     }
128     if (SDL_NumJoysticks() >= 2) {
129         if (!joy2 && SDL_JoystickOpened(1)) {
130             SDL_JoystickClose(joydev[1]);
131             joydev[1] = NULL;
132         }
133         if (joy2 && !SDL_JoystickOpened(1)) {
134             joydev[1] = SDL_JoystickOpen(1);
135         }
136     }
137 }
138 
is_joystick_action_active(int t,const joystick_action * a)139 static int is_joystick_action_active(int t, const joystick_action * a) {
140     if (a->type == 1) {
141         return SDL_JoystickGetButton(joydev[t], a->n);
142     } else if (a->type == 2) {
143         Sint16 v = SDL_JoystickGetAxis(joydev[t], a->n);
144         if (a->threshold_dir == 1)      /* upper bound */
145             return (v < a->threshold);
146         else
147             return (v > a->threshold);
148     } else {
149         return 0;
150     }
151 }
152 
153 /**
154  * Updates player action {*down,*up,*power,*roll,*guns,*bombs}
155  * according to current state of joystick t
156  * @param t index of joystick (0 or 1)
157  * @param inmenu = 1 if the player is in a menu (e.g. hangar menu)
158  */
get_joystick_action(int t,int inmenu,int * down,int * up,int * power,int * roll,int * guns,int * bombs)159 void get_joystick_action(int t, int inmenu, int *down, int *up, int *power, int *roll, int *guns, int *bombs) {
160     /*
161      * Special joystick actions for hangar menu are disabled here because
162      * they are unintuitive on gamepads and other non-default joystick
163      * configurations. Comment out next line to re-enable.
164      * FIXME perhaps make this a command-line option or something?
165      */
166     inmenu = 0;
167     if (inmenu) {
168         *down = is_joystick_action_active(t, &joystick_config[t].down);
169         *up = is_joystick_action_active(t, &joystick_config[t].up);
170         *power = is_joystick_action_active(t, &joystick_config[t].guns);
171         *roll = is_joystick_action_active(t, &joystick_config[t].bombs);
172         *guns = is_joystick_action_active(t, &joystick_config[t].power);
173         *bombs = is_joystick_action_active(t, &joystick_config[t].brake);
174     } else {
175         *roll = is_joystick_action_active(t, &joystick_config[t].roll);
176         *guns = is_joystick_action_active(t, &joystick_config[t].guns);
177         *bombs = is_joystick_action_active(t, &joystick_config[t].bombs);
178         if (is_joystick_action_active(t, &joystick_config[t].brake)) {
179             *power = 0;
180             *down = 1;
181             *up = 1;
182         } else {
183             *power = is_joystick_action_active(t, &joystick_config[t].power);
184             *down = is_joystick_action_active(t, &joystick_config[t].down);
185             *up = is_joystick_action_active(t, &joystick_config[t].up);
186         }
187     }
188 }
189 
190 /** Does joystick t have a roll button? */
joystick_has_roll_button(int t)191 int joystick_has_roll_button(int t) {
192     return (joystick_config[t].roll.type != 0);
193 }
194 
195 /** Allocate enough memory to hold state of axes of given joy. */
allocate_axis_state(int joy)196 Sint16 *allocate_axis_state(int joy) {
197     return (Sint16 *) walloc(SDL_JoystickNumAxes(joydev[joy]) * sizeof(Sint16));
198 }
199 
200 /** Save state of all axes of a given joystick */
save_axis_state(Sint16 * axes,int joy)201 void save_axis_state(Sint16 * axes, int joy) {
202     int i, num;
203 
204     num = SDL_JoystickNumAxes(joydev[joy]);
205     assert(num >= 0);
206 
207     for (i = 0; i < num; i++) {
208         axes[i] = SDL_JoystickGetAxis(joydev[joy], i);
209     }
210 }
211 
212 /* Find axis that has changed most between idle and current state */
find_changed_axis(struct joystick_action * act,Sint16 * idle,Sint16 * current,int joy)213 void find_changed_axis(struct joystick_action *act, Sint16 * idle, Sint16 * current, int joy) {
214     int i, num, max_index, max_value;
215 
216     num = SDL_JoystickNumAxes(joydev[joy]);
217     assert(num >= 0);
218 
219     max_index = 0;
220     max_value = abs(idle[0] - current[0]);
221     for (i = 1; i < num; i++) {
222         int val;
223         val = abs(idle[i] - current[i]);
224         if (val > max_value) {
225             max_value = val;
226             max_index = i;
227         }
228     }
229 
230     act->type = 2;
231     act->n = max_index;
232     if (idle[max_index] < current[max_index]) {
233         act->threshold_dir = 0; /* lower bound */
234     } else {
235         act->threshold_dir = 1; /* upper bound */
236     }
237     act->threshold = ((Sint16) (idle[max_index] + JOYSTICK_THRESHOLD_MULTIPLIER * ((double) current[max_index] - (double) idle[max_index])));
238 }
239 
240 /** Find button that is down. Returns 0 if no buttons are pressed. */
find_changed_button(struct joystick_action * act,int joy)241 int find_changed_button(struct joystick_action *act, int joy) {
242     int i, num;
243 
244     num = SDL_JoystickNumButtons(joydev[joy]);
245     assert(num >= 0);
246 
247     for (i = 0; i < num; i++) {
248         if (SDL_JoystickGetButton(joydev[joy], i)) {
249             act->type = 1;
250             act->n = i;
251             return 1;
252         }
253     }
254     return 0;
255 }
256 
set_disabled_action(struct joystick_action * act)257 void set_disabled_action(struct joystick_action *act) {
258     act->type = 0;
259 }
260 
get_joy_action_string(struct joystick_action * act)261 char *get_joy_action_string(struct joystick_action *act) {
262     char *buf = (char *) walloc(15);
263 
264     if (act->type == 0) {
265         strcpy(buf, "-");
266     } else if (act->type == 1) {
267         sprintf(buf, "B%d", act->n);
268     } else if (act->type == 2) {
269         sprintf(buf, "A%d%c", act->n, act->threshold_dir == 0 ? '+' : '-');
270     }
271     return buf;
272 }
273