UFO: Alien Invasion
Loading...
Searching...
No Matches
cl_joystick.cpp
Go to the documentation of this file.
1
4
5/*
6Copyright (C) 2002-2007 ioQuake3 team.
7Copyright (C) 2002-2025 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"
32
33static SDL_Joystick* stick = nullptr;
38
39static struct {
40 bool buttons[16];
41 unsigned int oldaxes;
42 unsigned int oldhats;
44
45/* We translate axes movement into keypresses */
57
58/* translate hat events into keypresses
59 * the 4 highest buttons are used for the first hat ... */
60static const int hat_keys[16] = {
69};
70
71void IN_JoystickMove (void)
72{
73 bool joy_pressed[lengthof(joy_keys)];
74 unsigned int axes = 0;
75 unsigned int hats = 0;
76
77 /* check whether a user has changed the joystick number */
78 if (in_joystickNo->modified)
80 /* check whether joysticks are disabled */
81 if (!in_joystick->integer)
82 return;
83
84 if (!stick)
85 return;
86
87 SDL_JoystickUpdate();
88
89 OBJZERO(joy_pressed);
90
91 /* update the ball state */
92 int total = SDL_JoystickNumBalls(stick);
93 if (total > 0) {
94 int balldx = 0;
95 int balldy = 0;
96 for (int i = 0; i < total; i++) {
97 int dx = 0;
98 int dy = 0;
99 SDL_JoystickGetBall(stick, i, &dx, &dy);
100 balldx += dx;
101 balldy += dy;
102 }
103 if (balldx || balldy) {
104 mousePosX = balldx / viddef.rx;
105 mousePosY = balldy / viddef.ry;
106 }
107 }
108
109 /* now query the stick buttons... */
110 total = SDL_JoystickNumButtons(stick);
111 if (total > 0) {
112 if (total > lengthof(stick_state.buttons))
113 total = lengthof(stick_state.buttons);
114 for (int i = 0; i < total; i++) {
115 const bool pressed = (SDL_JoystickGetButton(stick, i) != 0);
116 if (pressed != stick_state.buttons[i]) {
117 IN_EventEnqueue(K_JOY1 + i, 0, pressed);
118 stick_state.buttons[i] = pressed;
119 }
120 }
121 }
122
123 /* look at the hats... */
124 total = SDL_JoystickNumHats(stick);
125 if (total > 0) {
126 if (total > 4)
127 total = 4;
128 for (int i = 0; i < total; i++)
129 ((Uint8 *)&hats)[i] = SDL_JoystickGetHat(stick, i);
130 }
131
132 /* update hat state */
133 if (hats != stick_state.oldhats) {
134 for (int i = 0; i < 4; i++) {
135 if (((Uint8 *)&hats)[i] != ((Uint8 *)&stick_state.oldhats)[i]) {
136 /* release event */
137 switch (((Uint8 *)&stick_state.oldhats)[i]) {
138 case SDL_HAT_UP:
139 IN_EventEnqueue(hat_keys[4 * i + 0], 0, false);
140 break;
141 case SDL_HAT_RIGHT:
142 IN_EventEnqueue(hat_keys[4 * i + 1], 0, false);
143 break;
144 case SDL_HAT_DOWN:
145 IN_EventEnqueue(hat_keys[4 * i + 2], 0, false);
146 break;
147 case SDL_HAT_LEFT:
148 IN_EventEnqueue(hat_keys[4 * i + 3], 0, false);
149 break;
150 case SDL_HAT_RIGHTUP:
151 IN_EventEnqueue(hat_keys[4 * i + 0], 0, false);
152 IN_EventEnqueue(hat_keys[4 * i + 1], 0, false);
153 break;
154 case SDL_HAT_RIGHTDOWN:
155 IN_EventEnqueue(hat_keys[4 * i + 2], 0, false);
156 IN_EventEnqueue(hat_keys[4 * i + 1], 0, false);
157 break;
158 case SDL_HAT_LEFTUP:
159 IN_EventEnqueue(hat_keys[4 * i + 0], 0, false);
160 IN_EventEnqueue(hat_keys[4 * i + 3], 0, false);
161 break;
162 case SDL_HAT_LEFTDOWN:
163 IN_EventEnqueue(hat_keys[4 * i + 2], 0, false);
164 IN_EventEnqueue(hat_keys[4 * i + 3], 0, false);
165 break;
166 default:
167 break;
168 }
169 /* press event */
170 switch (((Uint8 *)&hats)[i]) {
171 case SDL_HAT_UP:
172 IN_EventEnqueue(hat_keys[4 * i + 0], 0, true);
173 break;
174 case SDL_HAT_RIGHT:
175 IN_EventEnqueue(hat_keys[4 * i + 1], 0, true);
176 break;
177 case SDL_HAT_DOWN:
178 IN_EventEnqueue(hat_keys[4 * i + 2], 0, true);
179 break;
180 case SDL_HAT_LEFT:
181 IN_EventEnqueue(hat_keys[4 * i + 3], 0, true);
182 break;
183 case SDL_HAT_RIGHTUP:
184 IN_EventEnqueue(hat_keys[4 * i + 0], 0, true);
185 IN_EventEnqueue(hat_keys[4 * i + 1], 0, true);
186 break;
187 case SDL_HAT_RIGHTDOWN:
188 IN_EventEnqueue(hat_keys[4 * i + 2], 0, true);
189 IN_EventEnqueue(hat_keys[4 * i + 1], 0, true);
190 break;
191 case SDL_HAT_LEFTUP:
192 IN_EventEnqueue(hat_keys[4 * i + 0], 0, true);
193 IN_EventEnqueue(hat_keys[4 * i + 3], 0, true);
194 break;
195 case SDL_HAT_LEFTDOWN:
196 IN_EventEnqueue(hat_keys[4 * i + 2], 0, true);
197 IN_EventEnqueue(hat_keys[4 * i + 3], 0, true);
198 break;
199 default:
200 break;
201 }
202 }
203 }
204 }
205
206 /* save hat state */
207 stick_state.oldhats = hats;
208
209 /* finally, look at the axes... */
210 total = SDL_JoystickNumAxes(stick);
211 if (total >= 2) {
212 /* the first two axes are used for the cursor movement */
213 for (int i = 0; i < 2; i++) {
214 const Sint16 axis = SDL_JoystickGetAxis(stick, i);
215 const float velocity = ((float) axis) / 32767.0f;
216 if (velocity > -in_joystickThreshold->value && velocity < in_joystickThreshold->value)
217 continue;
218
219 if (i & 1) {
220 mousePosY += in_joystickSpeed->value * velocity;
221 if (mousePosY > (int)viddef.context.height)
222 mousePosY = (int)viddef.context.height;
223 else if (mousePosY < 0)
224 mousePosY = 0;
225 } else {
226 mousePosX += in_joystickSpeed->value * velocity;
227 if (mousePosX > (int)viddef.context.width)
228 mousePosX = (int)viddef.context.width;
229 else if (mousePosX < 0)
230 mousePosX = 0;
231 }
232 }
233 }
234
235
236 if (total > 2) {
237 if (total > 16)
238 total = 16;
239 /* every axis except the first two can be normally bound to an action */
240 for (int i = 2; i < total; i++) {
241 const Sint16 axis = SDL_JoystickGetAxis(stick, i);
242 const float f = ((float) axis) / 32767.0f;
244 axes |= (1 << (i * 2));
245 } else if (f > in_joystickThreshold->value) {
246 axes |= (1 << ((i * 2) + 1));
247 }
248 }
249 }
250
251
252 /* Time to update axes state based on old vs. new. */
253 if (axes != stick_state.oldaxes) {
254 for (int i = 2; i < 16; i++) {
255 if ((axes & (1 << i)) && !(stick_state.oldaxes & (1 << i)))
256 IN_EventEnqueue(joy_keys[i], 0, true);
257
258 if (!(axes & (1 << i)) && (stick_state.oldaxes & (1 << i)))
259 IN_EventEnqueue(joy_keys[i], 0, false);
260 }
261 }
262
263 /* Save for future generations. */
264 stick_state.oldaxes = axes;
265}
266
271{
272 uiNode_t* joystickOptions = nullptr;
273 const int total = SDL_NumJoysticks();
274
275 if (total == 0) {
276 UI_AddOption(&joystickOptions, "", _("None"), "0");
277 } else {
278 for (int i = 0; i < total; i++)
279 UI_AddOption(&joystickOptions, "", SDL_JoystickNameForIndex(i), va("%i", i));
280 }
281 UI_RegisterOption(OPTION_JOYSTICKS, joystickOptions);
282}
283
288{
289 in_joystick = Cvar_Get("in_joystick", "0", CVAR_ARCHIVE, "Activate or deactivate the use of a joystick");
290 in_joystickNo = Cvar_Get("in_joystickNo", "0", CVAR_ARCHIVE, "Joystick to use - 0 is the first - 1 is the second ...");
291 in_joystickThreshold = Cvar_Get("in_joystickThreshold", "0.05", CVAR_ARCHIVE, "The threshold for the joystick axes");
292 in_joystickSpeed = Cvar_Get("in_joystickSpeed", "20", CVAR_ARCHIVE, "The joystick speed for the cursor");
293
294 if (stick != nullptr) {
295 Com_Printf("... closing already initialized joystick\n");
296 SDL_JoystickClose(stick);
297 }
298
299 stick = nullptr;
301
302 if (!SDL_WasInit(SDL_INIT_JOYSTICK)) {
303 Com_DPrintf(DEBUG_CLIENT, "Calling SDL_Init(SDL_INIT_JOYSTICK)...\n");
304 if (SDL_Init(SDL_INIT_JOYSTICK) == -1) {
305 Com_DPrintf(DEBUG_CLIENT, "SDL_Init(SDL_INIT_JOYSTICK) failed: %s\n", SDL_GetError());
306 return;
307 }
308 Com_DPrintf(DEBUG_CLIENT, "SDL_Init(SDL_INIT_JOYSTICK) passed.\n");
309 }
310
311 int total = SDL_NumJoysticks();
312 Com_Printf("%d possible joysticks\n", total);
313 for (int i = 0; i < total; i++)
314 Com_DPrintf(DEBUG_CLIENT, "[%d] %s\n", i, SDL_JoystickNameForIndex(i));
315
316 if (in_joystickNo->integer < 0 || in_joystickNo->integer >= total)
317 Cvar_Set("in_joystickNo", "0");
318 in_joystickNo->modified = false;
319
320 stick = SDL_JoystickOpen(in_joystickNo->integer);
321
322 if (stick == nullptr) {
323 Com_Printf("no joystick found.\n");
324 return;
325 }
326
327 Com_Printf("joystick %d opened - set cvar in_joystickNo to change this\n", in_joystickNo->integer);
328 Com_Printf("... name: %s\n", SDL_JoystickNameForIndex(in_joystickNo->integer));
329 Com_Printf("... axes: %d\n", SDL_JoystickNumAxes(stick));
330 Com_Printf("... hats: %d\n", SDL_JoystickNumHats(stick));
331 Com_Printf("... buttons: %d\n", SDL_JoystickNumButtons(stick));
332 Com_Printf("... balls: %d\n", SDL_JoystickNumBalls(stick));
333
334 SDL_JoystickEventState(SDL_QUERY);
335}
int mousePosY
Definition cl_input.cpp:76
void IN_EventEnqueue(unsigned int keyNum, unsigned short keyUnicode, bool keyDown)
Definition cl_input.cpp:832
int mousePosX
Definition cl_input.cpp:76
External (non-keyboard) input devices.
static cvar_t * in_joystick
static cvar_t * in_joystickSpeed
unsigned int oldaxes
static cvar_t * in_joystickNo
void IN_StartupJoystick(void)
Init available joysticks.
void IN_JoystickMove(void)
static const int hat_keys[16]
static SDL_Joystick * stick
static struct @224014336222201121242030200310257117217277365276 stick_state
static cvar_t * in_joystickThreshold
void IN_JoystickInitMenu(void)
Adds joysticks to the options menu.
unsigned int oldhats
bool buttons[16]
static const int joy_keys[16]
@ 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
Primary header for client.
void Com_DPrintf(int level, const char *fmt,...)
A Com_Printf that only shows up if the "developer" cvar is set.
Definition common.cpp:440
void Com_Printf(const char *const fmt,...)
Definition common.cpp:428
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
typedef int(ZCALLBACK *close_file_func) OF((voidpf opaque
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
Atomic structure used to define most of the UI.
Definition ui_nodes.h:80
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