added optional button to restart game (door, panel and touch variants)
[rocksndiamonds.git] / src / libgame / joystick.c
1 // ============================================================================
2 // Artsoft Retro-Game Library
3 // ----------------------------------------------------------------------------
4 // (c) 1995-2014 by Artsoft Entertainment
5 //                  Holger Schemel
6 //                  info@artsoft.org
7 //                  https://www.artsoft.org/
8 // ----------------------------------------------------------------------------
9 // joystick.c
10 // ============================================================================
11
12 #if defined(PLATFORM_FREEBSD)
13 #include <machine/joystick.h>
14 #endif
15
16 #include "joystick.h"
17 #include "misc.h"
18
19
20 // ============================================================================
21 // platform independent joystick functions
22 // ============================================================================
23
24 #define TRANSLATE_JOYSYMBOL_TO_JOYNAME  0
25 #define TRANSLATE_JOYNAME_TO_JOYSYMBOL  1
26
27 static void translate_joyname(int *joysymbol, char **name, int mode)
28 {
29   static struct
30   {
31     int joysymbol;
32     char *name;
33   } translate_joy[] =
34   {
35     { JOY_LEFT,         "joystick_left" },
36     { JOY_RIGHT,        "joystick_right" },
37     { JOY_UP,           "joystick_up" },
38     { JOY_DOWN,         "joystick_down" },
39     { JOY_BUTTON_1,     "joystick_button_1" },
40     { JOY_BUTTON_2,     "joystick_button_2" },
41   };
42
43   int i;
44
45   if (mode == TRANSLATE_JOYSYMBOL_TO_JOYNAME)
46   {
47     *name = "[undefined]";
48
49     for (i = 0; i < 6; i++)
50     {
51       if (*joysymbol == translate_joy[i].joysymbol)
52       {
53         *name = translate_joy[i].name;
54         break;
55       }
56     }
57   }
58   else if (mode == TRANSLATE_JOYNAME_TO_JOYSYMBOL)
59   {
60     *joysymbol = 0;
61
62     for (i = 0; i < 6; i++)
63     {
64       if (strEqual(*name, translate_joy[i].name))
65       {
66         *joysymbol = translate_joy[i].joysymbol;
67         break;
68       }
69     }
70   }
71 }
72
73 char *getJoyNameFromJoySymbol(int joysymbol)
74 {
75   char *name;
76
77   translate_joyname(&joysymbol, &name, TRANSLATE_JOYSYMBOL_TO_JOYNAME);
78   return name;
79 }
80
81 int getJoySymbolFromJoyName(char *name)
82 {
83   int joysymbol;
84
85   translate_joyname(&joysymbol, &name, TRANSLATE_JOYNAME_TO_JOYSYMBOL);
86   return joysymbol;
87 }
88
89 int getJoystickNrFromDeviceName(char *device_name)
90 {
91   char c;
92   int joystick_nr = 0;
93
94   if (device_name == NULL || device_name[0] == '\0')
95     return 0;
96
97   c = device_name[strlen(device_name) - 1];
98
99   if (c >= '0' && c <= '9')
100     joystick_nr = (int)(c - '0');
101
102   if (joystick_nr < 0 || joystick_nr >= MAX_PLAYERS)
103     joystick_nr = 0;
104
105   return joystick_nr;
106 }
107
108 char *getDeviceNameFromJoystickNr(int joystick_nr)
109 {
110   static char *joystick_device_name[MAX_PLAYERS] =
111   {
112     DEV_JOYSTICK_0,
113     DEV_JOYSTICK_1,
114     DEV_JOYSTICK_2,
115     DEV_JOYSTICK_3
116   };
117
118   return (joystick_nr >= 0 && joystick_nr <= 3 ?
119           joystick_device_name[joystick_nr] : "");
120 }
121
122 char *getFormattedJoystickName(const char *name_raw)
123 {
124   static char name[MAX_JOYSTICK_NAME_LEN + 1];
125   boolean name_skip_space = TRUE;
126   int i, j;
127
128   if (name_raw == NULL)
129     name_raw = "(unknown joystick)";
130
131   // copy joystick name, cutting leading and multiple spaces
132   for (i = 0, j = 0; i < strlen(name_raw) && i < MAX_JOYSTICK_NAME_LEN; i++)
133   {
134     if (name_raw[i] != ' ')
135     {
136       name[j++] = name_raw[i];
137       name_skip_space = FALSE;
138     }
139     else if (!name_skip_space)
140     {
141       name[j++] = name_raw[i];
142       name_skip_space = TRUE;
143     }
144   }
145
146   // cut trailing space
147   if (j > 0 && name[j - 1] == ' ')
148     j--;
149
150   name[j] = '\0';
151
152   return name;
153 }
154
155 static int JoystickPositionPercent(int center, int border, int actual)
156 {
157   int range, position;
158   int percent;
159
160   if (border < center && actual > center)
161     return 0;
162   if (border > center && actual < center)
163     return 0;
164
165   range = ABS(border - center);
166   position = ABS(actual - center);
167
168   percent = (int)(position * 100 / range);
169
170   if (percent > 100)
171     percent = 100;
172
173   return percent;
174 }
175
176 void CheckJoystickData(void)
177 {
178   int i;
179   int distance = 100;
180
181   for (i = 0; i < MAX_PLAYERS; i++)
182   {
183     if (setup.input[i].joy.xleft >= setup.input[i].joy.xmiddle)
184       setup.input[i].joy.xleft = setup.input[i].joy.xmiddle - distance;
185     if (setup.input[i].joy.xright <= setup.input[i].joy.xmiddle)
186       setup.input[i].joy.xright = setup.input[i].joy.xmiddle + distance;
187
188     if (setup.input[i].joy.yupper >= setup.input[i].joy.ymiddle)
189       setup.input[i].joy.yupper = setup.input[i].joy.ymiddle - distance;
190     if (setup.input[i].joy.ylower <= setup.input[i].joy.ymiddle)
191       setup.input[i].joy.ylower = setup.input[i].joy.ymiddle + distance;
192   }
193 }
194
195 int JoystickExt(int player_nr, boolean use_as_joystick_nr)
196 {
197   int joystick_nr = joystick.nr[player_nr];
198   int js_x, js_y;
199   boolean js_b1, js_b2;
200   int left, right, up, down;
201   int result = JOY_NO_ACTION;
202
203   if (use_as_joystick_nr)
204     joystick_nr = player_nr;
205
206   if (joystick.status != JOYSTICK_ACTIVATED)
207     return JOY_NO_ACTION;
208
209   if (joystick_nr < 0)
210     return JOY_NO_ACTION;
211
212   if (!ReadJoystick(joystick_nr, &js_x, &js_y, &js_b1, &js_b2))
213   {
214     Warn("cannot read joystick device '%s'",
215          setup.input[player_nr].joy.device_name);
216
217     joystick.status = JOYSTICK_NOT_AVAILABLE;
218
219     return JOY_NO_ACTION;
220   }
221
222   left  = JoystickPositionPercent(setup.input[player_nr].joy.xmiddle,
223                                   setup.input[player_nr].joy.xleft,  js_x);
224   right = JoystickPositionPercent(setup.input[player_nr].joy.xmiddle,
225                                   setup.input[player_nr].joy.xright, js_x);
226   up    = JoystickPositionPercent(setup.input[player_nr].joy.ymiddle,
227                                   setup.input[player_nr].joy.yupper, js_y);
228   down  = JoystickPositionPercent(setup.input[player_nr].joy.ymiddle,
229                                   setup.input[player_nr].joy.ylower, js_y);
230
231   if (left > JOYSTICK_PERCENT)
232     result |= JOY_LEFT;
233   else if (right > JOYSTICK_PERCENT)
234     result |= JOY_RIGHT;
235   if (up > JOYSTICK_PERCENT)
236     result |= JOY_UP;
237   else if (down > JOYSTICK_PERCENT)
238     result |= JOY_DOWN;
239
240   if (js_b1)
241     result |= JOY_BUTTON_1;
242   if (js_b2)
243     result |= JOY_BUTTON_2;
244
245   return result;
246 }
247
248 int Joystick(int player_nr)
249 {
250   return JoystickExt(player_nr, FALSE);
251 }
252
253 static int JoystickButtonExt(int player_nr, boolean use_as_joystick_nr)
254 {
255   static int last_joy_button[MAX_PLAYERS] = { 0, 0, 0, 0 };
256   int joy_button = (JoystickExt(player_nr, use_as_joystick_nr) & JOY_BUTTON);
257   int result;
258
259   if (joy_button)
260   {
261     if (last_joy_button[player_nr])
262       result = JOY_BUTTON_PRESSED;
263     else
264       result = JOY_BUTTON_NEW_PRESSED;
265   }
266   else
267   {
268     if (last_joy_button[player_nr])
269       result = JOY_BUTTON_NEW_RELEASED;
270     else
271       result = JOY_BUTTON_NOT_PRESSED;
272   }
273
274   last_joy_button[player_nr] = joy_button;
275   return result;
276 }
277
278 int JoystickButton(int player_nr)
279 {
280   return JoystickButtonExt(player_nr, FALSE);
281 }
282
283 int AnyJoystick(void)
284 {
285   int i;
286   int result = 0;
287
288   for (i = 0; i < MAX_PLAYERS; i++)
289     result |= JoystickExt(i, TRUE);
290
291   return result;
292 }
293
294 int AnyJoystickButton(void)
295 {
296   int i;
297   int result = JOY_BUTTON_NOT_PRESSED;
298
299   for (i = 0; i < MAX_PLAYERS; i++)
300   {
301     result = JoystickButtonExt(i, TRUE);
302     if (result != JOY_BUTTON_NOT_PRESSED)
303       break;
304   }
305
306   return result;
307 }
308
309 void DeactivateJoystick(void)
310 {
311   /* Temporarily deactivate joystick. This is needed for calibration
312      screens, where the player has to select a joystick device that
313      should be calibrated. If there is a totally uncalibrated joystick
314      active, it may be impossible (due to messed up input from joystick)
315      to select the joystick device to calibrate even when trying to use
316      the mouse or keyboard to select the device. */
317
318   if (joystick.status & JOYSTICK_AVAILABLE)
319     joystick.status &= ~JOYSTICK_ACTIVE;
320 }
321
322 void ActivateJoystick(void)
323 {
324   // reactivate temporarily deactivated joystick
325
326   if (joystick.status & JOYSTICK_AVAILABLE)
327     joystick.status |= JOYSTICK_ACTIVE;
328 }