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