TORCS  1.3.9
The Open Racing Car Simulator
joystickconfig.cpp
Go to the documentation of this file.
1 /***************************************************************************
2 
3  file : joystickconfig.cpp
4  created : Wed Mar 21 21:46:11 CET 2001
5  copyright : (C) 2001-2024 by Eric Espie, Bernhard Wymann
6  email : berniw@bluewin.ch
7 
8  ***************************************************************************/
9 
10 /***************************************************************************
11  * *
12  * This program is free software; you can redistribute it and/or modify *
13  * it under the terms of the GNU General Public License as published by *
14  * the Free Software Foundation; either version 2 of the License, or *
15  * (at your option) any later version. *
16  * *
17  ***************************************************************************/
18 
19 
20 #include <stdio.h>
21 #include <stdlib.h>
22 #include <tgfclient.h>
23 #include <track.h>
24 #include <robot.h>
25 #include "driverconfig.h"
26 #include <playerpref.h>
27 #include <portability.h>
28 #include <plib/js.h>
29 
30 #include "controlconfig.h"
31 #include "joystickconfig.h"
32 
33 static void *scrHandle2 = NULL;
34 
35 static tCmdInfo *Cmd;
36 static int maxCmd;
37 static void *parmHandle;
38 static const char* driverSection;
39 
40 static jsJoystick *js[NUM_JOY] = {NULL};
41 
42 static float ax[_JS_MAX_AXES * NUM_JOY] = {0};
43 static int rawb[NUM_JOY] = {0};
44 
45 // Variable to give a small delay for axis registration which might be overlapped by a button.
46 // This is e.g. the case for my ps4 gamepad, L2 and R2 are a button and an axis.
47 static double axisPressedTime = 0.0;
48 
49 #define NB_STEPS 6
50 
51 // TODO: refactor, this is horrible, depends on the order in controlconfig.cpp, static tCmdInfo Cmd[]
52 #define OFFSET_CMD 6
53 
54 static const char *Instructions[] = {
55  "Center the joystick then press a button for more than 0.2s",
56  "Steer left then press a button for more than 0.2s",
57  "Steer right then press a button for more than 0.2s",
58  "Apply full throttle then press a button for more than 0.2s",
59  "Apply full brake then press a button for more than 0.2s",
60  "Apply full clutch then press a button for more than 0.2s",
61  "Calibration successfully done",
62  "Calibration failed"
63 };
64 
65 static int CalState;
66 static int InstId;
67 
68 static const char *LabName[] = { "Steer", "Throttle", "Brake", "Clutch" };
69 static int LabAxisId[4];
70 static int LabMinId[4];
71 static int LabMaxId[4];
72 
73 
74 static void onBack(void *prevMenu)
75 {
76  GfuiScreenActivate(prevMenu);
77 }
78 
79 
80 static float axCenter[_JS_MAX_AXES * NUM_JOY];
81 
82 static void advanceStep (void)
83 {
84  do {
85  CalState++;
86  } while ((Cmd[CalState + OFFSET_CMD].ref.type != GFCTRL_TYPE_JOY_AXIS) && (CalState < NB_STEPS));
87 }
88 
89 
90 static void JoyCalAutomaton(void)
91 {
92  static int axis;
93  const int BUFSIZE = 1024;
94  char buf[BUFSIZE];
95 
96  axisPressedTime = 0.0;
97 
98  switch (CalState) {
99  case 0:
100  memcpy(axCenter, ax, sizeof(axCenter));
101  advanceStep();
102  break;
103  case 1:
104  axis = Cmd[CalState + OFFSET_CMD].ref.index;
105  Cmd[CalState + OFFSET_CMD].min = ax[axis];
106  Cmd[CalState + OFFSET_CMD].max = axCenter[axis];
107  Cmd[CalState + OFFSET_CMD].pow = 1.0;
108  snprintf(buf, BUFSIZE, "%.2g", ax[axis]);
110  advanceStep();
111  break;
112  case 2:
113  axis = Cmd[CalState + OFFSET_CMD].ref.index;
114  Cmd[CalState + OFFSET_CMD].min = axCenter[axis];
115  Cmd[CalState + OFFSET_CMD].max = ax[axis];
116  Cmd[CalState + OFFSET_CMD].pow = 1.0;
117  snprintf(buf, BUFSIZE, "%.2g", ax[axis]);
119  advanceStep();
120  break;
121  case 3:
122  case 4:
123  case 5:
124  axis = Cmd[CalState + OFFSET_CMD].ref.index;
125  Cmd[CalState + OFFSET_CMD].min = axCenter[axis];
126  Cmd[CalState + OFFSET_CMD].max = ax[axis];
127  Cmd[CalState + OFFSET_CMD].pow = 1.0;
128  snprintf(buf, BUFSIZE, "%.2g", axCenter[axis]);
130  snprintf(buf, BUFSIZE, "%.2g", ax[axis]);
132  advanceStep();
133  break;
134  }
136 }
137 
138 
139 static void Idle2(void)
140 {
141  int mask;
142  int b, i;
143  int index;
144  double currentTime = GfTimeClock();
145  const double delay = 0.2;
146 
147  for (index = 0; index < NUM_JOY; index++) {
148  if (js[index]) {
149  js[index]->read(&b, &ax[index * _JS_MAX_AXES]);
150 
151  if (currentTime - axisPressedTime > delay) {
152  /* Joystick buttons */
153  for (i = 0, mask = 1; i < 32; i++, mask *= 2) {
154  if (((b & mask) != 0) && ((rawb[index] & mask) == 0)) {
155  if (axisPressedTime == 0.0) {
156  axisPressedTime = currentTime;
157  } else {
158  const char *str = GfctrlGetNameByRef(GFCTRL_TYPE_JOY_BUT, i + 32 * index);
160  /* Button fired */
161  JoyCalAutomaton();
162  if (CalState >= NB_STEPS) {
163  glutIdleFunc(GfuiIdle);
164  }
165  glutPostRedisplay();
166  rawb[index] = b;
167  return;
168  }
169  }
170  }
171  }
172 
173  if (currentTime - axisPressedTime > delay) {
174  rawb[index] = b;
175  }
176  }
177  }
178  }
179 }
180 
181 
182 static void onActivate(void * /* dummy */)
183 {
184  int i;
185  int index;
186  int step;
187 
188  CalState = 0;
190  glutIdleFunc(Idle2);
191  glutPostRedisplay();
192  for (index = 0; index < NUM_JOY; index++) {
193  if (js[index]) {
194  js[index]->read(&rawb[index], &ax[index * _JS_MAX_AXES]); /* initial value */
195  }
196  }
197 
198  axisPressedTime = 0.0;
199 
200  for (i = 0; i < 4; i++) {
201  if (i > 0) {
202  step = i + 2;
203  } else {
204  step = i + 1;
205  }
206  if (Cmd[step + OFFSET_CMD].ref.type == GFCTRL_TYPE_JOY_AXIS) {
208  } else {
210  }
213  }
214 }
215 
216 
217 void *JoyCalMenuInit(void *prevMenu, tCmdInfo *cmd, int maxcmd, void *parmhandle, const char* driversection)
218 {
219  int x, y, dy, i, index;
220 
221  Cmd = cmd;
222  maxCmd = maxcmd;
223  parmHandle = parmhandle;
224  driverSection = driversection;
225 
226  if (scrHandle2) {
227  return scrHandle2;
228  }
229 
230  scrHandle2 = GfuiScreenCreateEx(NULL, NULL, onActivate, NULL, NULL, 1);
231  GfuiTitleCreate(scrHandle2, "Joystick Calibration", 0);
233 
234  GfuiScreenAddBgImg(scrHandle2, "data/img/splash-joycal.png");
235 
236  x = 128;
237  y = 300;
238  dy = 50;
239 
240  for (i = 0; i < 4; i++) {
245  y -= dy;
246  }
247 
248  for (index = 0; index < NUM_JOY; index++) {
249  if (js[index] == NULL) {
250  js[index] = new jsJoystick(index);
251  }
252 
253  if (js[index]->notWorking()) {
254  /* don't configure the joystick */
255  js[index] = NULL;
256  }
257  }
258 
260 
262  prevMenu, onBack, NULL, (tfuiCallback)NULL, (tfuiCallback)NULL);
263 
265  NULL, onActivate, NULL, (tfuiCallback)NULL, (tfuiCallback)NULL);
266 
267  return scrHandle2;
268 }
static const char * Instructions[]
#define GFUI_MOUSE_UP
Definition: tgfclient.h:80
int GfuiButtonCreate(void *scr, const char *text, int font, int x, int y, int width, int align, int mouse, void *userDataOnPush, tfuiCallback onPush, void *userDataOnFocus, tfuiCallback onFocus, tfuiCallback onFocusLost)
Add a button to a screen.
Definition: guibutton.cpp:248
float min
Definition: controlconfig.h:38
static float ax[_JS_MAX_AXES *NUM_JOY]
bool GfctrlIsEventBlacklisted(void *parmHandle, const char *driversSection, const char *event)
Check if given event is blacklisted (used for buttons or axis which fire a button AND move event)...
Definition: control.cpp:502
static void JoyCalAutomaton(void)
static void * scrHandle2
static void advanceStep(void)
Robot Module Interface Definition.
int GfuiLabelCreate(void *scr, const char *text, int font, int x, int y, int align, int maxlen)
Add a label to a screen.
Definition: guilabel.cpp:142
void GfuiScreenAddBgImg(void *scr, const char *filename)
Add an image background to a screen.
Definition: gui.cpp:961
static int rawb[NUM_JOY]
void GfuiIdle(void)
Idle function for the GUI to be called during Idle loop of glut.
Definition: gui.cpp:116
static void onBack(void *prevMenu)
static const char * LabName[]
The Gaming Framework API (client part).
static jsJoystick * js[NUM_JOY]
static int LabMaxId[4]
#define GFUI_FONT_LARGE
Definition: tgfclient.h:168
void * GfuiScreenCreateEx(float *bgColor, void *userDataOnActivate, tfuiCallback onActivate, void *userDataOnDeactivate, tfuiCallback onDeactivate, int mouseAllowed)
Create a screen.
Definition: gui.cpp:578
#define NB_STEPS
void GfuiLabelSetText(void *scr, int id, const char *text)
Change the text of a label.
Definition: guilabel.cpp:212
#define GFUI_ALIGN_HC_VC
Definition: tgfclient.h:73
static float axCenter[_JS_MAX_AXES *NUM_JOY]
const char * GfctrlGetNameByRef(GfCtrlType type, int index)
Get a control name by its reference.
Definition: control.cpp:191
static void Idle2(void)
void * JoyCalMenuInit(void *prevMenu, tCmdInfo *cmd, int maxcmd, void *parmhandle, const char *driversection)
static double axisPressedTime
Parameter set handle structure, multiple handles can reference the same parameter set...
Definition: params.cpp:125
void GfuiMenuDefaultKeysAdd(void *scr)
Add the default menu keyboard callback to a screen.
Definition: guimenu.cpp:55
static int LabMinId[4]
static int maxCmd
float max
Definition: controlconfig.h:40
void(* tfuiCallback)(void *)
Definition: tgfclient.h:105
void GfuiScreenActivate(void *screen)
Activate a screen and make it current.
Definition: gui.cpp:467
tCtrlRef ref
Definition: controlconfig.h:35
#define GFUI_ALIGN_HC_VB
Definition: tgfclient.h:72
double GfTimeClock(void)
Get the time in seconds.
Definition: os.cpp:50
static Vector y[4]
Definition: Convex.cpp:56
int GfuiTitleCreate(void *scr, const char *text, int maxlen)
Add a Title to the screen.
Definition: guilabel.cpp:170
static int InstId
static void onActivate(void *)
#define GFUI_FONT_MEDIUM
Definition: tgfclient.h:169
static const char * driverSection
#define OFFSET_CMD
static int LabAxisId[4]
static void * parmHandle
int index
Definition: tgfclient.h:266
Track Structure and Track Loader Module Definition.
static tCmdInfo * Cmd
#define NUM_JOY
Definition: tgfclient.h:35
float pow
Definition: controlconfig.h:42
static int CalState