Add support for new EV_BTN event in linux joystick

Adds support for new EV_BTN event that'll be used in place of EV_KEY for
joysticks and maybe gamepads. This event doesn't require any mapping as
it simply passes through button number as the code (starting from 1 just
like HID) and it's value.

It was needed to cleanly support joysticks and simracing/simflight
hardware that defines more than 80 buttons. Some HW used hacks in the
drivers that assigned random usages found lower than
TRIGGER_HAPPPY range.
This commit is contained in:
Tomasz Pakuła 2026-01-04 18:27:08 +01:00
parent 504041a1ba
commit 32b6590f56
3 changed files with 56 additions and 14 deletions

View file

@ -35,6 +35,9 @@
#ifndef KEY_ALS_TOGGLE
#define KEY_ALS_TOGGLE 0x230
#endif
#ifndef EV_BTN
#define EV_BTN 0x06
#endif
extern int
SDL_EVDEV_GuessDeviceClass(const unsigned long bitmask_props[NBITS(INPUT_PROP_MAX)],
@ -58,6 +61,11 @@ SDL_EVDEV_GuessDeviceClass(const unsigned long bitmask_props[NBITS(INPUT_PROP_MA
int devclass = 0;
unsigned long keyboard_mask;
// Only Joysticks (and maybe gamepads) have generic buttons
if (test_bit(EV_BTN, bitmask_ev)) {
return SDL_UDEV_DEVICE_JOYSTICK;
}
// If the kernel specifically says it's an accelerometer, believe it
if (test_bit(INPUT_PROP_ACCELEROMETER, bitmask_props)) {
return SDL_UDEV_DEVICE_ACCELEROMETER;

View file

@ -57,6 +57,10 @@
#ifndef SYN_DROPPED
#define SYN_DROPPED 3
#endif
#ifndef EV_BTN
#define EV_BTN 0x06
#define EVIOCGBTNCNT 0
#endif
#ifndef BTN_NORTH
#define BTN_NORTH 0x133
#endif
@ -1225,6 +1229,7 @@ static bool GuessIfAxesAreDigitalHat(struct input_absinfo *absinfo_x, struct inp
static void ConfigJoystick(SDL_Joystick *joystick, int fd, int fd_sensor)
{
int i, t;
unsigned long evbit[NBITS(EV_MAX)] = { 0 };
unsigned long keybit[NBITS(KEY_MAX)] = { 0 };
unsigned long absbit[NBITS(ABS_MAX)] = { 0 };
unsigned long relbit[NBITS(REL_MAX)] = { 0 };
@ -1238,27 +1243,40 @@ static void ConfigJoystick(SDL_Joystick *joystick, int fd, int fd_sensor)
// See if this device uses the new unified event API
if ((ioctl(fd, EVIOCGBIT(EV_KEY, sizeof(keybit)), keybit) >= 0) &&
(ioctl(fd, EVIOCGBIT(EV_ABS, sizeof(absbit)), absbit) >= 0) &&
(ioctl(fd, EVIOCGBIT(EV_REL, sizeof(relbit)), relbit) >= 0)) {
(ioctl(fd, EVIOCGBIT(EV_REL, sizeof(relbit)), relbit) >= 0) &&
(ioctl(fd, EVIOCGBIT(0, sizeof(evbit)), evbit) >= 0) ) {
// Get the number of buttons, axes, and other thingamajigs
for (i = BTN_JOYSTICK; i < KEY_MAX; ++i) {
if (test_bit(i, keybit)) {
if (test_bit(EV_BTN, evbit)) {
unsigned int button_count;
if(ioctl(fd, EVIOCGBTNCNT, &button_count) == 0) {
joystick->hwdata->ev_btn = true;
joystick->nbuttons = button_count;
#ifdef DEBUG_INPUT_EVENTS
SDL_Log("Joystick has button: 0x%x", i);
SDL_Log("Joystick has %u buttons", button_count);
#endif
joystick->hwdata->key_map[i] = joystick->nbuttons;
joystick->hwdata->has_key[i] = true;
++joystick->nbuttons;
}
}
for (i = 0; i < BTN_JOYSTICK; ++i) {
if (test_bit(i, keybit)) {
if (!joystick->hwdata->ev_btn) {
for (i = BTN_JOYSTICK; i < KEY_MAX; ++i) {
if (test_bit(i, keybit)) {
#ifdef DEBUG_INPUT_EVENTS
SDL_Log("Joystick has button: 0x%x", i);
SDL_Log("Joystick has button: 0x%x", i);
#endif
joystick->hwdata->key_map[i] = joystick->nbuttons;
joystick->hwdata->has_key[i] = true;
++joystick->nbuttons;
joystick->hwdata->key_map[i] = joystick->nbuttons;
joystick->hwdata->has_key[i] = true;
++joystick->nbuttons;
}
}
for (i = 0; i < BTN_JOYSTICK; ++i) {
if (test_bit(i, keybit)) {
#ifdef DEBUG_INPUT_EVENTS
SDL_Log("Joystick has button: 0x%x", i);
#endif
joystick->hwdata->key_map[i] = joystick->nbuttons;
joystick->hwdata->has_key[i] = true;
++joystick->nbuttons;
}
}
}
for (i = ABS_HAT0X; i <= ABS_HAT3Y; i += 2) {
@ -1860,7 +1878,8 @@ static void PollAllValues(Uint64 timestamp, SDL_Joystick *joystick)
// Poll all buttons
SDL_zeroa(keyinfo);
if (ioctl(joystick->hwdata->fd, EVIOCGKEY(sizeof(keyinfo)), keyinfo) >= 0) {
if (ioctl(joystick->hwdata->fd, EVIOCGKEY(sizeof(keyinfo)), keyinfo) >= 0 &&
!joystick->hwdata->ev_btn) {
for (i = 0; i < KEY_MAX; i++) {
if (joystick->hwdata->has_key[i]) {
bool down = test_bit(i, keyinfo);
@ -1963,7 +1982,18 @@ static void HandleInputEvents(SDL_Joystick *joystick)
}
switch (event->type) {
case EV_BTN:
if (!joystick->hwdata->ev_btn)
break;
#ifdef DEBUG_INPUT_EVENTS
SDL_Log("Button %u %s", code, event->value ? "PRESSED" : "RELEASED");
#endif
SDL_SendJoystickButton(SDL_EVDEV_GetEventTimestamp(event), joystick,
code - 1, (event->value != 0));
break;
case EV_KEY:
if (joystick->hwdata->ev_btn)
break;
#ifdef DEBUG_INPUT_EVENTS
SDL_Log("Key 0x%.2x %s", code, event->value ? "PRESSED" : "RELEASED");
#endif
@ -2061,6 +2091,7 @@ static void HandleInputEvents(SDL_Joystick *joystick)
}
switch (event->type) {
case EV_BTN:
case EV_KEY:
SDL_assert(0);
break;

View file

@ -63,6 +63,9 @@ struct joystick_hwdata
bool has_accelerometer;
bool has_gyro;
// Use EV_BTN instead of EV_KEY
bool ev_btn;
// Support for the classic joystick interface
bool classic;
Uint16 *key_pam;