UFO: Alien Invasion
cl_joystick.cpp
Go to the documentation of this file.
1
5/*
6Copyright (C) 2002-2007 ioQuake3 team.
7Copyright (C) 2002-2022 UFO: Alien Invasion.
8
9This program is free software; you can redistribute it and/or
10modify it under the terms of the GNU General Public License
11as published by the Free Software Foundation; either version 2
12of the License, or (at your option) any later version.
13
14This program is distributed in the hope that it will be useful,
15but WITHOUT ANY WARRANTY; without even the implied warranty of
16MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
17
18See the GNU General Public License for more details.
19
20You should have received a copy of the GNU General Public License
21along with this program; if not, write to the Free Software
22Foundation, 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
33static SDL_Joystick* stick = nullptr;
38
39#if SDL_VERSION_ATLEAST(2,0,0)
40#define SDL_JoystickName SDL_JoystickNameForIndex
41#endif
42
43static struct {
44 bool buttons[16];
45 unsigned int oldaxes;
46 unsigned int oldhats;
48
49/* We translate axes movement into keypresses */
50static 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 ... */
64static const int hat_keys[16] = {
73};
74
75void 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 */
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)
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}
int mousePosY
Definition: cl_input.cpp:80
void IN_EventEnqueue(unsigned int keyNum, unsigned short keyUnicode, bool keyDown)
Definition: cl_input.cpp:905
int mousePosX
Definition: cl_input.cpp:80
External (non-keyboard) input devices.
static struct @11 stick_state
static cvar_t * in_joystick
Definition: cl_joystick.cpp:34
static cvar_t * in_joystickSpeed
Definition: cl_joystick.cpp:37
unsigned int oldaxes
Definition: cl_joystick.cpp:45
static cvar_t * in_joystickNo
Definition: cl_joystick.cpp:35
void IN_StartupJoystick(void)
Init available joysticks.
void IN_JoystickMove(void)
Definition: cl_joystick.cpp:75
static const int hat_keys[16]
Definition: cl_joystick.cpp:64
static SDL_Joystick * stick
Definition: cl_joystick.cpp:33
static cvar_t * in_joystickThreshold
Definition: cl_joystick.cpp:36
void IN_JoystickInitMenu(void)
Adds joysticks to the options menu.
unsigned int oldhats
Definition: cl_joystick.cpp:46
bool buttons[16]
Definition: cl_joystick.cpp:44
static const int joy_keys[16]
Definition: cl_joystick.cpp:50
@ K_JOY19
Definition: cl_keys.h:119
@ K_JOY22
Definition: cl_keys.h:122
@ K_JOY21
Definition: cl_keys.h:121
@ K_JOY32
Definition: cl_keys.h:132
@ K_JOY25
Definition: cl_keys.h:125
@ K_JOY17
Definition: cl_keys.h:117
@ K_JOY23
Definition: cl_keys.h:123
@ K_JOY30
Definition: cl_keys.h:130
@ K_JOY16
Definition: cl_keys.h:116
@ K_UPARROW
Definition: cl_keys.h:72
@ K_JOY18
Definition: cl_keys.h:118
@ K_JOY31
Definition: cl_keys.h:131
@ K_JOY29
Definition: cl_keys.h:129
@ K_JOY28
Definition: cl_keys.h:128
@ K_JOY26
Definition: cl_keys.h:126
@ K_JOY27
Definition: cl_keys.h:127
@ K_JOY20
Definition: cl_keys.h:120
@ K_JOY24
Definition: cl_keys.h:124
@ K_RIGHTARROW
Definition: cl_keys.h:74
@ K_DOWNARROW
Definition: cl_keys.h:73
@ K_JOY1
Definition: cl_keys.h:101
@ K_LEFTARROW
Definition: cl_keys.h:75
#define _(String)
Definition: cl_shared.h:44
viddef_t viddef
Definition: cl_video.cpp:34
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
void Com_Printf(const char *const fmt,...)
Definition: common.cpp:386
cvar_t * Cvar_Set(const char *varName, const char *value,...)
Sets a cvar value.
Definition: cvar.cpp:615
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 CVAR_ARCHIVE
Definition: cvar.h:40
#define DEBUG_CLIENT
Definition: defines.h:59
QGL_EXTERN GLfloat f
Definition: r_gl.h:114
QGL_EXTERN GLint i
Definition: r_gl.h:113
#define OBJZERO(obj)
Definition: shared.h:178
#define lengthof(x)
Definition: shared.h:105
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
bool modified
Definition: cvar.h:79
float value
Definition: cvar.h:80
int integer
Definition: cvar.h:81
Atomic structure used to define most of the UI.
Definition: ui_nodes.h:80
float ry
Definition: cl_video.h:72
float rx
Definition: cl_video.h:71
viddefContext_t context
Definition: cl_video.h:67
unsigned width
Definition: cl_video.h:44
unsigned height
Definition: cl_video.h:45
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
@ OPTION_JOYSTICKS
Definition: ui_dataids.h:76