From 32b6590f560e2b6ac3b6e8f8ccf2d662363492c8 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tomasz=20Paku=C5=82a?= Date: Sun, 4 Jan 2026 18:27:08 +0100 Subject: [PATCH 1/2] 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. --- src/core/linux/SDL_evdev_capabilities.c | 8 ++++ src/joystick/linux/SDL_sysjoystick.c | 59 +++++++++++++++++++------ src/joystick/linux/SDL_sysjoystick_c.h | 3 ++ 3 files changed, 56 insertions(+), 14 deletions(-) diff --git a/src/core/linux/SDL_evdev_capabilities.c b/src/core/linux/SDL_evdev_capabilities.c index 38c891ce7f..2c0c523f26 100644 --- a/src/core/linux/SDL_evdev_capabilities.c +++ b/src/core/linux/SDL_evdev_capabilities.c @@ -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; diff --git a/src/joystick/linux/SDL_sysjoystick.c b/src/joystick/linux/SDL_sysjoystick.c index 14c9debfab..267aa4fa6d 100644 --- a/src/joystick/linux/SDL_sysjoystick.c +++ b/src/joystick/linux/SDL_sysjoystick.c @@ -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; diff --git a/src/joystick/linux/SDL_sysjoystick_c.h b/src/joystick/linux/SDL_sysjoystick_c.h index d149948c8e..06adcb07be 100644 --- a/src/joystick/linux/SDL_sysjoystick_c.h +++ b/src/joystick/linux/SDL_sysjoystick_c.h @@ -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; From 28671b7be3198399e29becc51597fe55d17cc89f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tomasz=20Paku=C5=82a?= Date: Sun, 4 Jan 2026 20:04:43 +0100 Subject: [PATCH 2/2] Fix some debug prints that caused errors in linux joystick --- src/joystick/linux/SDL_sysjoystick.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/joystick/linux/SDL_sysjoystick.c b/src/joystick/linux/SDL_sysjoystick.c index 267aa4fa6d..eef9511551 100644 --- a/src/joystick/linux/SDL_sysjoystick.c +++ b/src/joystick/linux/SDL_sysjoystick.c @@ -1925,7 +1925,7 @@ static void PollAllSensors(Uint64 timestamp, SDL_Joystick *joystick) if (ioctl(joystick->hwdata->fd_sensor, EVIOCGABS(ABS_RX + i), &absinfo) >= 0) { values[i] = absinfo.value * (SDL_PI_F / 180.f) / joystick->hwdata->gyro_scale[i]; #ifdef DEBUG_INPUT_EVENTS - SDL_Log("Joystick : Re-read Gyro (axis %d) val= %f", i, data[i]); + SDL_Log("Joystick : Re-read Gyro (axis %d) val= %f", i, values[i]); #endif } } @@ -1939,7 +1939,7 @@ static void PollAllSensors(Uint64 timestamp, SDL_Joystick *joystick) if (ioctl(joystick->hwdata->fd_sensor, EVIOCGABS(ABS_X + i), &absinfo) >= 0) { values[i] = absinfo.value * SDL_STANDARD_GRAVITY / joystick->hwdata->accelerometer_scale[i]; #ifdef DEBUG_INPUT_EVENTS - SDL_Log("Joystick : Re-read Accelerometer (axis %d) val= %f", i, data[i]); + SDL_Log("Joystick : Re-read Accelerometer (axis %d) val= %f", i, values[i]); #endif } }