UFO: Alien Invasion
 All Data Structures Namespaces Files Functions Variables Typedefs Enumerations Enumerator Friends Macros Modules Pages
cl_joystick.cpp
Go to the documentation of this file.
1 
5 /*
6 Copyright (C) 2002-2007 ioQuake3 team.
7 Copyright (C) 2002-2020 UFO: Alien Invasion.
8 
9 This program is free software; you can redistribute it and/or
10 modify it under the terms of the GNU General Public License
11 as published by the Free Software Foundation; either version 2
12 of the License, or (at your option) any later version.
13 
14 This program is distributed in the hope that it will be useful,
15 but WITHOUT ANY WARRANTY; without even the implied warranty of
16 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
17 
18 See the GNU General Public License for more details.
19 
20 You should have received a copy of the GNU General Public License
21 along with this program; if not, write to the Free Software
22 Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
23 
24 */
25 
26 #include "cl_joystick.h"
27 #include "../client.h"
28 #include "cl_input.h"
29 #include "../ui/ui_main.h"
30 #include "../ui/ui_nodes.h"
31 #include "../ui/node/ui_node_abstractoption.h"
32 
33 static SDL_Joystick* stick = nullptr;
38 
39 #if SDL_VERSION_ATLEAST(2,0,0)
40 #define SDL_JoystickName SDL_JoystickNameForIndex
41 #endif
42 
43 static struct {
44  bool buttons[16];
45  unsigned int oldaxes;
46  unsigned int oldhats;
47 } stick_state;
48 
49 /* We translate axes movement into keypresses */
50 static const int joy_keys[16] = {
57 
60 };
61 
62 /* translate hat events into keypresses
63  * the 4 highest buttons are used for the first hat ... */
64 static const int hat_keys[16] = {
73 };
74 
75 void IN_JoystickMove (void)
76 {
77  bool joy_pressed[lengthof(joy_keys)];
78  unsigned int axes = 0;
79  unsigned int hats = 0;
80 
81  /* check whether a user has changed the joystick number */
82  if (in_joystickNo->modified)
84  /* check whether joysticks are disabled */
85  if (!in_joystick->integer)
86  return;
87 
88  if (!stick)
89  return;
90 
91  SDL_JoystickUpdate();
92 
93  OBJZERO(joy_pressed);
94 
95  /* update the ball state */
96  int total = SDL_JoystickNumBalls(stick);
97  if (total > 0) {
98  int balldx = 0;
99  int balldy = 0;
100  for (int i = 0; i < total; i++) {
101  int dx = 0;
102  int dy = 0;
103  SDL_JoystickGetBall(stick, i, &dx, &dy);
104  balldx += dx;
105  balldy += dy;
106  }
107  if (balldx || balldy) {
108  mousePosX = balldx / viddef.rx;
109  mousePosY = balldy / viddef.ry;
110  }
111  }
112 
113  /* now query the stick buttons... */
114  total = SDL_JoystickNumButtons(stick);
115  if (total > 0) {
116  if (total > lengthof(stick_state.buttons))
117  total = lengthof(stick_state.buttons);
118  for (int i = 0; i < total; i++) {
119  const bool pressed = (SDL_JoystickGetButton(stick, i) != 0);
120  if (pressed != stick_state.buttons[i]) {
121  IN_EventEnqueue(K_JOY1 + i, 0, pressed);
122  stick_state.buttons[i] = pressed;
123  }
124  }
125  }
126 
127  /* look at the hats... */
128  total = SDL_JoystickNumHats(stick);
129  if (total > 0) {
130  if (total > 4)
131  total = 4;
132  for (int i = 0; i < total; i++)
133  ((Uint8 *)&hats)[i] = SDL_JoystickGetHat(stick, i);
134  }
135 
136  /* update hat state */
137  if (hats != stick_state.oldhats) {
138  for (int i = 0; i < 4; i++) {
139  if (((Uint8 *)&hats)[i] != ((Uint8 *)&stick_state.oldhats)[i]) {
140  /* release event */
141  switch (((Uint8 *)&stick_state.oldhats)[i]) {
142  case SDL_HAT_UP:
143  IN_EventEnqueue(hat_keys[4 * i + 0], 0, false);
144  break;
145  case SDL_HAT_RIGHT:
146  IN_EventEnqueue(hat_keys[4 * i + 1], 0, false);
147  break;
148  case SDL_HAT_DOWN:
149  IN_EventEnqueue(hat_keys[4 * i + 2], 0, false);
150  break;
151  case SDL_HAT_LEFT:
152  IN_EventEnqueue(hat_keys[4 * i + 3], 0, false);
153  break;
154  case SDL_HAT_RIGHTUP:
155  IN_EventEnqueue(hat_keys[4 * i + 0], 0, false);
156  IN_EventEnqueue(hat_keys[4 * i + 1], 0, false);
157  break;
158  case SDL_HAT_RIGHTDOWN:
159  IN_EventEnqueue(hat_keys[4 * i + 2], 0, false);
160  IN_EventEnqueue(hat_keys[4 * i + 1], 0, false);
161  break;
162  case SDL_HAT_LEFTUP:
163  IN_EventEnqueue(hat_keys[4 * i + 0], 0, false);
164  IN_EventEnqueue(hat_keys[4 * i + 3], 0, false);
165  break;
166  case SDL_HAT_LEFTDOWN:
167  IN_EventEnqueue(hat_keys[4 * i + 2], 0, false);
168  IN_EventEnqueue(hat_keys[4 * i + 3], 0, false);
169  break;
170  default:
171  break;
172  }
173  /* press event */
174  switch (((Uint8 *)&hats)[i]) {
175  case SDL_HAT_UP:
176  IN_EventEnqueue(hat_keys[4 * i + 0], 0, true);
177  break;
178  case SDL_HAT_RIGHT:
179  IN_EventEnqueue(hat_keys[4 * i + 1], 0, true);
180  break;
181  case SDL_HAT_DOWN:
182  IN_EventEnqueue(hat_keys[4 * i + 2], 0, true);
183  break;
184  case SDL_HAT_LEFT:
185  IN_EventEnqueue(hat_keys[4 * i + 3], 0, true);
186  break;
187  case SDL_HAT_RIGHTUP:
188  IN_EventEnqueue(hat_keys[4 * i + 0], 0, true);
189  IN_EventEnqueue(hat_keys[4 * i + 1], 0, true);
190  break;
191  case SDL_HAT_RIGHTDOWN:
192  IN_EventEnqueue(hat_keys[4 * i + 2], 0, true);
193  IN_EventEnqueue(hat_keys[4 * i + 1], 0, true);
194  break;
195  case SDL_HAT_LEFTUP:
196  IN_EventEnqueue(hat_keys[4 * i + 0], 0, true);
197  IN_EventEnqueue(hat_keys[4 * i + 3], 0, true);
198  break;
199  case SDL_HAT_LEFTDOWN:
200  IN_EventEnqueue(hat_keys[4 * i + 2], 0, true);
201  IN_EventEnqueue(hat_keys[4 * i + 3], 0, true);
202  break;
203  default:
204  break;
205  }
206  }
207  }
208  }
209 
210  /* save hat state */
211  stick_state.oldhats = hats;
212 
213  /* finally, look at the axes... */
214  total = SDL_JoystickNumAxes(stick);
215  if (total >= 2) {
216  /* the first two axes are used for the cursor movement */
217  for (int i = 0; i < 2; i++) {
218  const Sint16 axis = SDL_JoystickGetAxis(stick, i);
219  const float velocity = ((float) axis) / 32767.0f;
220  if (velocity > -in_joystickThreshold->value && velocity < in_joystickThreshold->value)
221  continue;
222 
223  if (i & 1) {
224  mousePosY += in_joystickSpeed->value * velocity;
225  if (mousePosY > (int)viddef.context.height)
227  else if (mousePosY < 0)
228  mousePosY = 0;
229  } else {
230  mousePosX += in_joystickSpeed->value * velocity;
231  if (mousePosX > (int)viddef.context.width)
232  mousePosX = (int)viddef.context.width;
233  else if (mousePosX < 0)
234  mousePosX = 0;
235  }
236  }
237  }
238 
239 
240  if (total > 2) {
241  if (total > 16)
242  total = 16;
243  /* every axis except the first two can be normally bound to an action */
244  for (int i = 2; i < total; i++) {
245  const Sint16 axis = SDL_JoystickGetAxis(stick, i);
246  const float f = ((float) axis) / 32767.0f;
247  if (f < -in_joystickThreshold->value) {
248  axes |= (1 << (i * 2));
249  } else if (f > in_joystickThreshold->value) {
250  axes |= (1 << ((i * 2) + 1));
251  }
252  }
253  }
254 
255 
256  /* Time to update axes state based on old vs. new. */
257  if (axes != stick_state.oldaxes) {
258  for (int i = 2; i < 16; i++) {
259  if ((axes & (1 << i)) && !(stick_state.oldaxes & (1 << i)))
260  IN_EventEnqueue(joy_keys[i], 0, true);
261 
262  if (!(axes & (1 << i)) && (stick_state.oldaxes & (1 << i)))
263  IN_EventEnqueue(joy_keys[i], 0, false);
264  }
265  }
266 
267  /* Save for future generations. */
268  stick_state.oldaxes = axes;
269 }
270 
275 {
276  uiNode_t* joystickOptions = nullptr;
277  const int total = SDL_NumJoysticks();
278 
279  if (total == 0) {
280  UI_AddOption(&joystickOptions, "", _("None"), "0");
281  } else {
282  for (int i = 0; i < total; i++)
283  UI_AddOption(&joystickOptions, "", SDL_JoystickName(i), va("%i", i));
284  }
285  UI_RegisterOption(OPTION_JOYSTICKS, joystickOptions);
286 }
287 
292 {
293  in_joystick = Cvar_Get("in_joystick", "0", CVAR_ARCHIVE, "Activate or deactivate the use of a joystick");
294  in_joystickNo = Cvar_Get("in_joystickNo", "0", CVAR_ARCHIVE, "Joystick to use - 0 is the first - 1 is the second ...");
295  in_joystickThreshold = Cvar_Get("in_joystickThreshold", "0.05", CVAR_ARCHIVE, "The threshold for the joystick axes");
296  in_joystickSpeed = Cvar_Get("in_joystickSpeed", "20", CVAR_ARCHIVE, "The joystick speed for the cursor");
297 
298  if (stick != nullptr) {
299  Com_Printf("... closing already initialized joystick\n");
300  SDL_JoystickClose(stick);
301  }
302 
303  stick = nullptr;
305 
306  if (!SDL_WasInit(SDL_INIT_JOYSTICK)) {
307  Com_DPrintf(DEBUG_CLIENT, "Calling SDL_Init(SDL_INIT_JOYSTICK)...\n");
308  if (SDL_Init(SDL_INIT_JOYSTICK) == -1) {
309  Com_DPrintf(DEBUG_CLIENT, "SDL_Init(SDL_INIT_JOYSTICK) failed: %s\n", SDL_GetError());
310  return;
311  }
312  Com_DPrintf(DEBUG_CLIENT, "SDL_Init(SDL_INIT_JOYSTICK) passed.\n");
313  }
314 
315  int total = SDL_NumJoysticks();
316  Com_Printf("%d possible joysticks\n", total);
317  for (int i = 0; i < total; i++)
318  Com_DPrintf(DEBUG_CLIENT, "[%d] %s\n", i, SDL_JoystickName(i));
319 
320  if (in_joystickNo->integer < 0 || in_joystickNo->integer >= total)
321  Cvar_Set("in_joystickNo", "0");
322  in_joystickNo->modified = false;
323 
324  stick = SDL_JoystickOpen(in_joystickNo->integer);
325 
326  if (stick == nullptr) {
327  Com_Printf("no joystick found.\n");
328  return;
329  }
330 
331  Com_Printf("joystick %d opened - set cvar in_joystickNo to change this\n", in_joystickNo->integer);
332  Com_Printf("... name: %s\n", SDL_JoystickName(in_joystickNo->integer));
333  Com_Printf("... axes: %d\n", SDL_JoystickNumAxes(stick));
334  Com_Printf("... hats: %d\n", SDL_JoystickNumHats(stick));
335  Com_Printf("... buttons: %d\n", SDL_JoystickNumButtons(stick));
336  Com_Printf("... balls: %d\n", SDL_JoystickNumBalls(stick));
337 
338  SDL_JoystickEventState(SDL_QUERY);
339 }
unsigned int oldhats
Definition: cl_joystick.cpp:46
const char * va(const char *format,...)
does a varargs printf into a temp buffer, so I don't need to have varargs versions of all text functi...
Definition: shared.cpp:410
This is a cvar definition. Cvars can be user modified and used in our menus e.g.
Definition: cvar.h:71
#define _(String)
Definition: cl_shared.h:43
static const int joy_keys[16]
Definition: cl_joystick.cpp:50
float rx
Definition: cl_video.h:71
float value
Definition: cvar.h:80
viddef_t viddef
Definition: cl_video.cpp:34
unsigned width
Definition: cl_video.h:44
void Com_Printf(const char *const fmt,...)
Definition: common.cpp:386
static cvar_t * in_joystickSpeed
Definition: cl_joystick.cpp:37
int integer
Definition: cvar.h:81
#define CVAR_ARCHIVE
Definition: cvar.h:40
void IN_EventEnqueue(unsigned int keyNum, unsigned short keyUnicode, bool keyDown)
Definition: cl_input.cpp:905
static cvar_t * in_joystickNo
Definition: cl_joystick.cpp:35
#define DEBUG_CLIENT
Definition: defines.h:59
cvar_t * Cvar_Get(const char *var_name, const char *var_value, int flags, const char *desc)
Init or return a cvar.
Definition: cvar.cpp:342
#define OBJZERO(obj)
Definition: shared.h:178
int mousePosY
Definition: cl_input.cpp:80
External (non-keyboard) input devices.
static const int hat_keys[16]
Definition: cl_joystick.cpp:64
void Com_DPrintf(int level, const char *fmt,...)
A Com_Printf that only shows up if the "developer" cvar is set.
Definition: common.cpp:398
viddefContext_t context
Definition: cl_video.h:67
Atomic structure used to define most of the UI.
Definition: ui_nodes.h:80
static cvar_t * in_joystick
Definition: cl_joystick.cpp:34
QGL_EXTERN GLfloat f
Definition: r_gl.h:114
static struct @11 stick_state
void IN_JoystickInitMenu(void)
Adds joysticks to the options menu.
QGL_EXTERN GLint i
Definition: r_gl.h:113
void IN_StartupJoystick(void)
Init available joysticks.
void IN_JoystickMove(void)
Definition: cl_joystick.cpp:75
void UI_RegisterOption(int dataId, uiNode_t *option)
Definition: ui_data.cpp:311
uiNode_t * UI_AddOption(uiNode_t **tree, const char *name, const char *label, const char *value)
Append an option to an option list.
Definition: ui_data.cpp:172
unsigned int oldaxes
Definition: cl_joystick.cpp:45
#define lengthof(x)
Definition: shared.h:105
cvar_t * Cvar_Set(const char *varName, const char *value,...)
Sets a cvar value.
Definition: cvar.cpp:615
bool modified
Definition: cvar.h:79
unsigned height
Definition: cl_video.h:45
bool buttons[16]
Definition: cl_joystick.cpp:44
float ry
Definition: cl_video.h:72
int mousePosX
Definition: cl_input.cpp:80
static SDL_Joystick * stick
Definition: cl_joystick.cpp:33
static cvar_t * in_joystickThreshold
Definition: cl_joystick.cpp:36