42static const PairDef kKnownPairs[] =
53static const int kNumKnownPairs =
sizeof(kKnownPairs) /
sizeof(kKnownPairs[0]);
65 joystick_ev =
new js_event();
88 joystick_fd = open(dev_path, O_RDONLY | O_NONBLOCK);
92 ioctl(joystick_fd, JSIOCGNAME(256), name);
93 ioctl(joystick_fd, JSIOCGVERSION, &version);
94 ioctl(joystick_fd, JSIOCGAXES, &axes);
95 ioctl(joystick_fd, JSIOCGBUTTONS, &buttons);
97 joystick_st->
axis.resize(axes);
98 joystick_st->
button.resize(buttons);
106 const int ABS_BUF_SIZE = 64;
107 uint8_t axmapBuf[ABS_BUF_SIZE] = {};
108 m_axmap.resize(axes, 0);
109 if (ioctl(joystick_fd, JSIOCGAXMAP, axmapBuf) < 0)
112 for (
int i = 0; i < axes; i++)
113 m_axmap[i] =
static_cast<uint8_t
>(i);
117 for (
int i = 0; i < axes; i++)
118 m_axmap[i] = axmapBuf[i];
126 m_calCenter.assign(axes, 0);
127 m_calMin.assign(axes, -32767);
128 m_calMax.assign(axes, 32767);
131 m_axisReversed.assign(axes,
false);
143 pthread_join(thread,
nullptr);
154void JoyStickDriver::buildAxisPairs()
156 m_stickPairs.clear();
157 m_axisToPairIndex.assign(axes, -1);
159 for (
int p = 0; p < kNumKnownPairs; p++)
161 const PairDef &def = kKnownPairs[p];
162 int idx0 = -1, idx1 = -1;
164 for (
int i = 0; i < axes; i++)
166 if (m_axmap[i] == def.absX) idx0 = i;
167 if (m_axmap[i] == def.absY) idx1 = i;
170 if (idx0 >= 0 && idx1 >= 0)
173 if (m_axisToPairIndex[idx0] >= 0 || m_axisToPairIndex[idx1] >= 0)
179 pair.
absX = def.absX;
180 pair.
absY = def.absY;
181 pair.
isHat = def.isHat;
182 pair.
name = def.name;
184 int pairIdx =
static_cast<int>(m_stickPairs.size());
185 m_stickPairs.push_back(pair);
186 m_axisToPairIndex[idx0] = pairIdx;
187 m_axisToPairIndex[idx1] = pairIdx;
193 if (m_stickPairs.empty())
195 for (
int i = 0; i + 1 < axes; i += 2)
200 pair.
absX =
static_cast<uint8_t
>(i);
201 pair.
absY =
static_cast<uint8_t
>(i + 1);
203 pair.
name =
"Joystick";
205 int pairIdx =
static_cast<int>(m_stickPairs.size());
206 m_stickPairs.push_back(pair);
207 m_axisToPairIndex[i] = pairIdx;
208 m_axisToPairIndex[i + 1] = pairIdx;
217JoyAxisType JoyStickDriver::classifyAbsCode(uint8_t code)
248const char *JoyStickDriver::absCodeLabel(uint8_t code)
253 return "Left-Stick X";
255 return "Left-Stick Y";
259 return "Right-Stick X";
261 return "Right-Stick Y";
314 snprintf(dev_path, 256,
"%s", port);
340 if (!m_stickPairs.empty())
341 return static_cast<uint8_t
>(m_stickPairs.size());
345 if (axes % 2 != 0) n++;
346 return static_cast<uint8_t
>(n);
351 if (axis < 0 || axis >=
static_cast<int>(m_axmap.size()))
353 return m_axmap[axis];
358 if (axis < 0 || axis >=
static_cast<int>(m_axmap.size()))
360 return classifyAbsCode(m_axmap[axis]);
365 if (axis < 0 || axis >=
static_cast<int>(m_axmap.size()))
367 return absCodeLabel(m_axmap[axis]);
372 if (n < 0 || n >=
static_cast<int>(m_stickPairs.size()))
374 return &m_stickPairs[n];
379 if (axis < 0 || axis >=
static_cast<int>(m_calCenter.size()))
381 m_calCenter[axis] = center;
382 m_calMin[axis] = minVal;
383 m_calMax[axis] = maxVal;
388 if (axis < 0 || axis >=
static_cast<int>(m_axisReversed.size()))
390 m_axisReversed[axis] = reversed;
395 return n > -1 && n < buttons ? joystick_st->
button[n] : 0;
411 int bytes = read(joystick_fd, joystick_ev,
sizeof(*joystick_ev));
414 joystick_ev->type &= ~JS_EVENT_INIT;
416 if (joystick_ev->type & JS_EVENT_BUTTON)
418 const int n = joystick_ev->number;
419 if (n <
static_cast<int>(joystick_st->
button.size()))
421 joystick_st->
button[n] = joystick_ev->value;
426 if (joystick_ev->type & JS_EVENT_AXIS)
428 const int axIdx = joystick_ev->number;
429 if (axIdx >=
static_cast<int>(joystick_st->
axis.size()))
432 joystick_st->
axis[axIdx] = joystick_ev->value;
438 if (axIdx <
static_cast<int>(m_axisToPairIndex.size()))
440 const int pairIdx = m_axisToPairIndex[axIdx];
458 usleep(pollMS * 1000);
465float JoyStickDriver::normalizeAxis(
int value,
int idx)
const
467 if (idx < 0 || idx >=
static_cast<int>(m_calCenter.size()))
468 return value / 32767.0f;
471 if (idx <
static_cast<int>(m_axisReversed.size()) && m_axisReversed[idx])
474 const int center = m_calCenter[idx];
477 const int span = m_calMax[idx] - center;
478 if (span <= 0)
return 0.0f;
479 return std::min(1.0f,
static_cast<float>(value - center) /
static_cast<float>(span));
483 const int span = center - m_calMin[idx];
484 if (span <= 0)
return 0.0f;
485 return std::max(-1.0f,
static_cast<float>(value - center) /
static_cast<float>(span));
496 pos.
theta = pos.
r = pos.
x = pos.
y = 0.0f;
498 if (n < 0 || n >=
static_cast<int>(m_stickPairs.size()))
502 const int i0 = pair.
i0;
503 const int i1 = pair.
i1;
505 if (i0 >=
static_cast<int>(joystick_st->
axis.size()) ||
506 i1 >=
static_cast<int>(joystick_st->
axis.size()))
511 const float x0 = normalizeAxis(joystick_st->
axis[i0], i0);
512 const float y0 = -normalizeAxis(joystick_st->
axis[i1], i1);
516 const float x = x0 * std::sqrt(1.0f - 0.5f * y0 * y0);
517 const float y = y0 * std::sqrt(1.0f - 0.5f * x0 * x0);
521 pos.
r = std::sqrt(x * x + y * y);
527 pos.
theta = std::atan2(y, x) * (180.0f / 3.141592653589793f);
528 if (pos.
theta < 0.0f)
The JoyStickDriver class provides basic functionality to read events from supported game pads under L...
buttonFunc buttonCallbackFunc
static void buttonEvent(int button_n, int value)
uint8_t getNumOfJoysticks()
getNumOfJoysticks Returns the number of proper analog-stick / hat pairs found via JSIOCGAXMAP....
JoyAxisType getAxisType(int axis) const
getAxisType Returns the classified type of axis.
uint8_t getAxisAbsCode(int axis) const
getAxisAbsCode Returns the ABS_* event code for axis, or 0xFF if the axis map is not available.
const JoyAxisPair * getJoystickPair(int n) const
getJoystickPair Returns the axis-pair descriptor for joystick n, or nullptr if out of range.
uint8_t getNumrOfButtons()
static void * loop(void *obj)
void setAxisCalibration(int axis, int center, int minVal, int maxVal)
setAxisCalibration Set piecewise-linear calibration for one axis.
static void axisEvent(int axis_n, int value)
void setButtonCallback(buttonFunc buttonCallback)
void setAxisReversed(int axis, bool reversed)
setAxisReversed Enable or disable reversal for a single axis.
axisFunc axisCallbackFunc
joystickFunc joystickCallbackFunc
std::function< void(int axis_n, double value)> axisFunc
std::function< void(int joystick_n, double mag, double angle)> joystickFunc
void setAxisCallback(axisFunc axisCallback)
static void joystickEvent(int joystick_n, double mag, double angle)
void setJoystickCallback(joystickFunc joystickCallback)
bool buttonPressed(int n)
void setPort(const char *port)
const char * getAxisLabel(int axis) const
getAxisLabel Returns a human-readable label for axis (e.g. "Left-Stick X", "L2 Trigger",...
std::function< void(int button_n, int value)> buttonFunc
joystick_position joystickPosition(int n)
JoyAxisType
Axis type classification inferred from JSIOCGAXMAP.
@ HAT
Digital hat / d-pad (ABS_HAT0X/Y … ABS_HAT3Y)
@ STICK
Analog stick (ABS_X/Y, ABS_RX/RY, throttle, rudder, wheel)
@ TRIGGER
Analog trigger — single-axis input (ABS_Z/RZ, gas, brake)
#define ABS_RZ
R2 / right trigger.
#define ABS_Z
L2 / left trigger.
A matched pair of axis indices that together form a joystick.
bool isHat
True for digital hat / d-pad pairs.
const char * name
Human-readable name (e.g. "Left Stick")
uint8_t absY
ABS_* code for the Y axis.
int i0
Index of the horizontal (X) axis.
uint8_t absX
ABS_* code for the X axis.
int i1
Index of the vertical (Y) axis.
std::vector< signed short > axis
std::vector< signed short > button