Compare commits

...

2 Commits

Author SHA1 Message Date
Bananymous c84a30d4dd ports/SDL2: Update to new joystick interface 2026-01-07 19:07:42 +02:00
Bananymous 24d91eee90 Kernel/LibInput: Rework Joystick handling
Joystick axis and buttons are now named to standard values, this allows
interfacing multiple different controllers (only DS3 is supported)

Add ioctl calls for userspace to set joystick player leds and rumble

Only use DS3 code paths when we detect that the attached device is
actually an DS3 controller

update test-joystick program to the new interface and add support to
control rumble and player leds
2026-01-07 19:01:07 +02:00
6 changed files with 405 additions and 121 deletions

View File

@ -12,6 +12,12 @@ namespace Kernel
BAN_NON_COPYABLE(USBJoystick);
BAN_NON_MOVABLE(USBJoystick);
enum class Type
{
Unknown,
DualShock3,
};
public:
BAN::ErrorOr<void> initialize() override;
@ -22,20 +28,42 @@ namespace Kernel
void handle_variable(uint16_t usage_page, uint16_t usage, int64_t state) override;
void handle_variable_absolute(uint16_t usage_page, uint16_t usage, int64_t state, int64_t min, int64_t max) override;
void update() override;
protected:
BAN::ErrorOr<size_t> read_impl(off_t, BAN::ByteSpan) override;
bool can_read_impl() const override { return true; }
BAN::ErrorOr<long> ioctl_impl(int request, void* arg) override;
private:
USBJoystick(USBHIDDriver&);
~USBJoystick() = default;
private:
void initialize_type();
BAN::ErrorOr<void> update_dualshock3_state(uint8_t led_bitmap, uint8_t rumble_strength);
private:
USBHIDDriver& m_driver;
Type m_type { Type::Unknown };
BAN::UniqPtr<DMARegion> m_send_buffer;
SpinLock m_state_lock;
InterruptState m_interrupt_state;
LibInput::JoystickState m_state {};
LibInput::JoystickState m_state;
size_t m_state_index { 0 };
BAN::Atomic<bool> m_has_got_report { false };
Mutex m_command_mutex;
BAN::Atomic<bool> m_has_initialized_leds { false };
uint8_t m_led_state { 0b0001 };
uint8_t m_rumble_strength { 0x00 };
friend class BAN::RefPtr<USBJoystick>;
};

View File

@ -1,6 +1,9 @@
#include <kernel/Input/InputDevice.h>
#include <kernel/Lock/LockGuard.h>
#include <kernel/USB/HID/Joystick.h>
#include <sys/ioctl.h>
namespace Kernel
{
@ -8,35 +11,64 @@ namespace Kernel
: USBHIDDevice(InputDevice::Type::Joystick)
, m_driver(driver)
{
using namespace LibInput;
m_state.axis[JSA_TRIGGER_LEFT] = -32767;
m_state.axis[JSA_TRIGGER_RIGHT] = -32767;
}
void USBJoystick::initialize_type()
{
m_type = Type::Unknown;
const auto& device_descriptor = m_driver.device().device_descriptor();
switch (device_descriptor.idVendor)
{
case 0x054C: // Sony
switch (device_descriptor.idProduct)
{
case 0x0268: // DualShock 3
m_type = Type::DualShock3;
break;
}
break;
}
}
BAN::ErrorOr<void> USBJoystick::initialize()
{
// TODO: this is not a generic USB HID joystick driver but one for PS3 controller.
// this may still work with other HID joysticks so i won't limit this only
// based on the device.
initialize_type();
// linux hid-sony.c
auto temp_region = TRY(DMARegion::create(17));
m_send_buffer = TRY(DMARegion::create(PAGE_SIZE));
USBDeviceRequest request;
switch (m_type)
{
case Type::Unknown:
break;
case Type::DualShock3:
{
// linux hid-sony.c
// move ps3 controller to "operational" state
request.bmRequestType = USB::RequestType::DeviceToHost | USB::RequestType::Class | USB::RequestType::Interface;
request.bRequest = 0x01;
request.wValue = 0x03F2;
request.wIndex = m_driver.interface().descriptor.bInterfaceNumber;
request.wLength = 17;
TRY(m_driver.device().send_request(request, temp_region->paddr()));
TRY(m_driver.device().send_request(request, m_send_buffer->paddr()));
// some compatible controllers need this too
// some compatible controllers need this too (we don't detect compatible controllers though)
request.bmRequestType = USB::RequestType::DeviceToHost | USB::RequestType::Class | USB::RequestType::Interface;
request.bRequest = 0x01;
request.wValue = 0x03F5;
request.wIndex = m_driver.interface().descriptor.bInterfaceNumber;
request.wLength = 8;
TRY(m_driver.device().send_request(request, temp_region->paddr()));
TRY(m_driver.device().send_request(request, m_send_buffer->paddr()));
break;
}
}
return {};
}
@ -44,16 +76,13 @@ namespace Kernel
void USBJoystick::start_report()
{
m_interrupt_state = m_state_lock.lock();
for (auto& axis : m_state.axis)
axis = {};
for (auto& button : m_state.buttons)
button = false;
m_state_index = 0;
}
void USBJoystick::stop_report()
{
m_state_lock.unlock(m_interrupt_state);
m_has_got_report = true;
}
void USBJoystick::handle_array(uint16_t usage_page, uint16_t usage)
@ -71,31 +100,76 @@ namespace Kernel
void USBJoystick::handle_variable_absolute(uint16_t usage_page, uint16_t usage, int64_t state, int64_t min, int64_t max)
{
using namespace LibInput;
constexpr auto map_joystick_axis =
[](int64_t value, int64_t min, int64_t max) -> int16_t
{
if (min == max)
return 0;
return (value - min) * 65534 / (max - min) - 32767;
};
constexpr auto map_trigger_axis =
[](int64_t value, int64_t min, int64_t max) -> int16_t
{
if (min == max)
return -32767;
return (value - min) * 65534 / (max - min) - 32767;
};
static constexpr JoystickButton button_map[] {
[ 0] = JSB_SELECT,
[ 1] = JSB_STICK_LEFT,
[ 2] = JSB_STICK_RIGHT,
[ 3] = JSB_START,
[ 4] = JSB_DPAD_UP,
[ 5] = JSB_DPAD_RIGHT,
[ 6] = JSB_DPAD_DOWN,
[ 7] = JSB_DPAD_LEFT,
[ 8] = JSB_COUNT, // left trigger
[ 9] = JSB_COUNT, // right trigger
[10] = JSB_SHOULDER_LEFT,
[11] = JSB_SHOULDER_RIGHT,
[12] = JSB_FACE_UP,
[13] = JSB_FACE_RIGHT,
[14] = JSB_FACE_DOWN,
[15] = JSB_FACE_LEFT,
[16] = JSB_MENU,
};
switch (usage_page)
{
case 0x01:
// TODO: These are probably only correct for dualshock 3
switch (usage)
{
case 0x01:
// TODO: PS3 controller sends some extra data with this usage
if (m_state_index == 8)
m_state.axis[JSA_TRIGGER_LEFT] = map_trigger_axis(state, min, max);
if (m_state_index == 9)
m_state.axis[JSA_TRIGGER_RIGHT] = map_trigger_axis(state, min, max);
m_state_index++;
break;
case 0x30:
m_state.axis[0] = { state, min, max };
m_state.axis[JSA_STICK_LEFT_X] = map_joystick_axis(state, min, max);
break;
case 0x31:
m_state.axis[1] = { state, min, max };
m_state.axis[JSA_STICK_LEFT_Y] = map_joystick_axis(state, min, max);
break;
case 0x32:
m_state.axis[2] = { state, min, max };
m_state.axis[JSA_STICK_RIGHT_X] = map_joystick_axis(state, min, max);
break;
case 0x35:
m_state.axis[3] = { state, min, max };
m_state.axis[JSA_STICK_RIGHT_Y] = map_joystick_axis(state, min, max);
break;
}
break;
case 0x09:
if (usage > 0 && usage <= sizeof(m_state.buttons))
m_state.buttons[usage - 1] = state;
// TODO: These are probably only correct for dualshock 3
if (usage > 0 && usage <= sizeof(button_map) / sizeof(*button_map))
if (const auto button = button_map[usage - 1]; button != JSB_COUNT)
m_state.buttons[button] = state;
break;
default:
dprintln("Unsupported absolute usage page {2H}", usage_page);
@ -111,4 +185,112 @@ namespace Kernel
return to_copy;
}
BAN::ErrorOr<long> USBJoystick::ioctl_impl(int request, void* arg)
{
switch (request)
{
case JOYSTICK_GET_LEDS:
*static_cast<uint8_t*>(arg) = m_led_state;
return 0;
case JOYSTICK_SET_LEDS:
switch (m_type)
{
case Type::Unknown:
return BAN::Error::from_errno(ENOTSUP);
case Type::DualShock3:
TRY(update_dualshock3_state(*static_cast<const uint8_t*>(arg), m_rumble_strength));
return 0;
}
ASSERT_NOT_REACHED();
case JOYSTICK_GET_RUMBLE:
*static_cast<uint32_t*>(arg) = m_rumble_strength;
return 0;
case JOYSTICK_SET_RUMBLE:
switch (m_type)
{
case Type::Unknown:
return BAN::Error::from_errno(ENOTSUP);
case Type::DualShock3:
TRY(update_dualshock3_state(m_led_state, *static_cast<const uint32_t*>(arg)));
return 0;
}
ASSERT_NOT_REACHED();
}
return USBHIDDevice::ioctl(request, arg);
}
void USBJoystick::update()
{
if (!m_has_got_report)
return;
switch (m_type)
{
case Type::Unknown:
break;
case Type::DualShock3:
// DualShock 3 only accepts leds after it has started sending reports
// (when you press the PS button)
if (!m_has_initialized_leds)
(void)update_dualshock3_state(m_led_state, m_rumble_strength);
break;
}
}
BAN::ErrorOr<void> USBJoystick::update_dualshock3_state(uint8_t led_state, uint8_t rumble_strength)
{
led_state &= 0x0F;
LockGuard _(m_command_mutex);
// we cannot do anything until we have received an reports
if (!m_has_got_report)
{
m_led_state = led_state;
m_rumble_strength = rumble_strength;
return {};
}
auto* request_data = reinterpret_cast<uint8_t*>(m_send_buffer->vaddr());
memset(request_data, 0, 35);
// header
request_data[0] = 0x01; // report id (?)
request_data[1] = 0xFF; // no idea but linux sets this, doesn't seem to affect anything
// first byte is maybe *enable rumble control*, it has to be non-zero for rumble to do anything
request_data[3] = 0xFF;
request_data[4] = rumble_strength;
// LED bitmap (bit 1: led 1, bit 2: led 2, ...)
request_data[9] = led_state << 1;
// No idea what these do but they need to be correct for the corresponding led to turn on.
// Also they are in reverse order, first entry corresponds to led 4, second to led 3, ...
for (size_t i = 0; i < 4; i++)
{
// values are the same as linux sends
request_data[10 + i * 5] = 0xFF; // has to be non zero
request_data[11 + i * 5] = 0x27; // ignored
request_data[12 + i * 5] = 0x10; // has to be non zero
request_data[13 + i * 5] = 0x00; // ignored
request_data[14 + i * 5] = 0x32; // has to be non zero
}
USBDeviceRequest request;
request.bmRequestType = USB::RequestType::HostToDevice | USB::RequestType::Class | USB::RequestType::Interface;
request.bRequest = 0x09;
request.wValue = 0x0201;
request.wIndex = m_driver.interface().descriptor.bInterfaceNumber;
request.wLength = 35;
TRY(m_driver.device().send_request(request, m_send_buffer->paddr()));
m_led_state = led_state;
m_rumble_strength = rumble_strength;
m_has_initialized_leds = true;
return {};
}
}

View File

@ -1,6 +1,6 @@
diff -ruN SDL2-2.32.8/cmake/sdlplatform.cmake SDL2-2.32.8-banan_os/cmake/sdlplatform.cmake
--- SDL2-2.32.8/cmake/sdlplatform.cmake 2024-08-14 13:35:43.000000000 +0300
+++ SDL2-2.32.8-banan_os/cmake/sdlplatform.cmake 2026-01-03 19:50:45.777185459 +0200
+++ SDL2-2.32.8-banan_os/cmake/sdlplatform.cmake 2026-01-07 19:04:34.332166371 +0200
@@ -28,6 +28,8 @@
set(SDL_CMAKE_PLATFORM AIX)
elseif(CMAKE_SYSTEM_NAME MATCHES "Minix.*")
@ -12,7 +12,7 @@ diff -ruN SDL2-2.32.8/cmake/sdlplatform.cmake SDL2-2.32.8-banan_os/cmake/sdlplat
endif()
diff -ruN SDL2-2.32.8/CMakeLists.txt SDL2-2.32.8-banan_os/CMakeLists.txt
--- SDL2-2.32.8/CMakeLists.txt 2025-06-03 02:00:39.000000000 +0300
+++ SDL2-2.32.8-banan_os/CMakeLists.txt 2026-01-03 19:51:02.494165302 +0200
+++ SDL2-2.32.8-banan_os/CMakeLists.txt 2026-01-07 19:04:34.343116295 +0200
@@ -14,7 +14,7 @@
set(SDL2_SUBPROJECT ON)
endif()
@ -98,7 +98,7 @@ diff -ruN SDL2-2.32.8/CMakeLists.txt SDL2-2.32.8-banan_os/CMakeLists.txt
file(GLOB MISC_SOURCES ${SDL2_SOURCE_DIR}/src/misc/riscos/*.c)
diff -ruN SDL2-2.32.8/include/SDL_config.h.cmake SDL2-2.32.8-banan_os/include/SDL_config.h.cmake
--- SDL2-2.32.8/include/SDL_config.h.cmake 2025-01-01 17:47:53.000000000 +0200
+++ SDL2-2.32.8-banan_os/include/SDL_config.h.cmake 2026-01-03 19:55:22.679890924 +0200
+++ SDL2-2.32.8-banan_os/include/SDL_config.h.cmake 2026-01-07 19:04:34.358682129 +0200
@@ -307,6 +307,7 @@
#cmakedefine SDL_AUDIO_DRIVER_FUSIONSOUND @SDL_AUDIO_DRIVER_FUSIONSOUND@
#cmakedefine SDL_AUDIO_DRIVER_FUSIONSOUND_DYNAMIC @SDL_AUDIO_DRIVER_FUSIONSOUND_DYNAMIC@
@ -125,7 +125,7 @@ diff -ruN SDL2-2.32.8/include/SDL_config.h.cmake SDL2-2.32.8-banan_os/include/SD
#cmakedefine SDL_VIDEO_DRIVER_DIRECTFB @SDL_VIDEO_DRIVER_DIRECTFB@
diff -ruN SDL2-2.32.8/include/SDL_platform.h SDL2-2.32.8-banan_os/include/SDL_platform.h
--- SDL2-2.32.8/include/SDL_platform.h 2025-01-01 17:47:53.000000000 +0200
+++ SDL2-2.32.8-banan_os/include/SDL_platform.h 2026-01-03 19:50:45.778818009 +0200
+++ SDL2-2.32.8-banan_os/include/SDL_platform.h 2026-01-07 19:04:34.370086235 +0200
@@ -36,6 +36,10 @@
#undef __HAIKU__
#define __HAIKU__ 1
@ -139,7 +139,7 @@ diff -ruN SDL2-2.32.8/include/SDL_platform.h SDL2-2.32.8-banan_os/include/SDL_pl
#define __BSDI__ 1
diff -ruN SDL2-2.32.8/src/audio/banan_os/SDL_banan_os_audio.cpp SDL2-2.32.8-banan_os/src/audio/banan_os/SDL_banan_os_audio.cpp
--- SDL2-2.32.8/src/audio/banan_os/SDL_banan_os_audio.cpp 1970-01-01 02:00:00.000000000 +0200
+++ SDL2-2.32.8-banan_os/src/audio/banan_os/SDL_banan_os_audio.cpp 2026-01-03 19:50:45.779146754 +0200
+++ SDL2-2.32.8-banan_os/src/audio/banan_os/SDL_banan_os_audio.cpp 2026-01-07 19:04:34.370691623 +0200
@@ -0,0 +1,150 @@
+/*
+ Simple DirectMedia Layer
@ -293,7 +293,7 @@ diff -ruN SDL2-2.32.8/src/audio/banan_os/SDL_banan_os_audio.cpp SDL2-2.32.8-bana
+#endif
diff -ruN SDL2-2.32.8/src/audio/banan_os/SDL_banan_os_audio.h SDL2-2.32.8-banan_os/src/audio/banan_os/SDL_banan_os_audio.h
--- SDL2-2.32.8/src/audio/banan_os/SDL_banan_os_audio.h 1970-01-01 02:00:00.000000000 +0200
+++ SDL2-2.32.8-banan_os/src/audio/banan_os/SDL_banan_os_audio.h 2026-01-03 19:50:45.779253053 +0200
+++ SDL2-2.32.8-banan_os/src/audio/banan_os/SDL_banan_os_audio.h 2026-01-07 19:04:34.370883199 +0200
@@ -0,0 +1,34 @@
+/*
+ Simple DirectMedia Layer
@ -331,7 +331,7 @@ diff -ruN SDL2-2.32.8/src/audio/banan_os/SDL_banan_os_audio.h SDL2-2.32.8-banan_
+};
diff -ruN SDL2-2.32.8/src/audio/SDL_audio.c SDL2-2.32.8-banan_os/src/audio/SDL_audio.c
--- SDL2-2.32.8/src/audio/SDL_audio.c 2025-01-01 17:47:53.000000000 +0200
+++ SDL2-2.32.8-banan_os/src/audio/SDL_audio.c 2026-01-03 19:50:45.779381073 +0200
+++ SDL2-2.32.8-banan_os/src/audio/SDL_audio.c 2026-01-07 19:04:34.371410923 +0200
@@ -87,6 +87,9 @@
#ifdef SDL_AUDIO_DRIVER_HAIKU
&HAIKUAUDIO_bootstrap,
@ -344,7 +344,7 @@ diff -ruN SDL2-2.32.8/src/audio/SDL_audio.c SDL2-2.32.8-banan_os/src/audio/SDL_a
#endif
diff -ruN SDL2-2.32.8/src/audio/SDL_sysaudio.h SDL2-2.32.8-banan_os/src/audio/SDL_sysaudio.h
--- SDL2-2.32.8/src/audio/SDL_sysaudio.h 2025-01-01 17:47:53.000000000 +0200
+++ SDL2-2.32.8-banan_os/src/audio/SDL_sysaudio.h 2026-01-03 19:50:45.779975705 +0200
+++ SDL2-2.32.8-banan_os/src/audio/SDL_sysaudio.h 2026-01-07 19:04:34.372150756 +0200
@@ -196,6 +196,7 @@
extern AudioBootStrap WINMM_bootstrap;
extern AudioBootStrap PAUDIO_bootstrap;
@ -355,8 +355,8 @@ diff -ruN SDL2-2.32.8/src/audio/SDL_sysaudio.h SDL2-2.32.8-banan_os/src/audio/SD
extern AudioBootStrap DUMMYAUDIO_bootstrap;
diff -ruN SDL2-2.32.8/src/joystick/banan_os/SDL_banan_os_joystick.cpp SDL2-2.32.8-banan_os/src/joystick/banan_os/SDL_banan_os_joystick.cpp
--- SDL2-2.32.8/src/joystick/banan_os/SDL_banan_os_joystick.cpp 1970-01-01 02:00:00.000000000 +0200
+++ SDL2-2.32.8-banan_os/src/joystick/banan_os/SDL_banan_os_joystick.cpp 2026-01-03 19:51:21.437329388 +0200
@@ -0,0 +1,308 @@
+++ SDL2-2.32.8-banan_os/src/joystick/banan_os/SDL_banan_os_joystick.cpp 2026-01-07 19:07:12.677617077 +0200
@@ -0,0 +1,296 @@
+/*
+Simple DirectMedia Layer
+Copyright (C) 1997-2025 Sam Lantinga <slouken@libsdl.org>
@ -408,8 +408,6 @@ diff -ruN SDL2-2.32.8/src/joystick/banan_os/SDL_banan_os_joystick.cpp SDL2-2.32.
+static int s_joystick_count = 0;
+static char* s_joystick_path[MAX_JOYSTICKS];
+
+static void BANANOS_JoystickDetect(void);
+
+static int BANANOS_JoystickInit(void)
+{
+ s_joystick_count = 0;
@ -498,8 +496,8 @@ diff -ruN SDL2-2.32.8/src/joystick/banan_os/SDL_banan_os_joystick.cpp SDL2-2.32.
+ return SDL_SetError("Unable to open joystick");
+ }
+
+ joystick->nbuttons = sizeof(LibInput::JoystickState::buttons) / sizeof(*LibInput::JoystickState::buttons);
+ joystick->naxes = sizeof(LibInput::JoystickState::axis) / sizeof(*LibInput::JoystickState::axis);
+ joystick->nbuttons = LibInput::JSB_COUNT;
+ joystick->naxes = LibInput::JSA_COUNT;
+ joystick->nhats = 0;
+
+ return 0;
@ -511,17 +509,8 @@ diff -ruN SDL2-2.32.8/src/joystick/banan_os/SDL_banan_os_joystick.cpp SDL2-2.32.
+ if (read(joystick->hwdata->fd, &state, sizeof(state)) < static_cast<ssize_t>(sizeof(state)))
+ return;
+
+ const auto map_joystick_value =
+ [](const LibInput::JoystickState::Axis& axis) -> Sint16
+ {
+ if (axis.min == axis.max)
+ return 0;
+ const float mapped = (axis.value - axis.min) * 65534.0f / (axis.max - axis.min) - 32767.0f;
+ return BAN::Math::clamp<Sint32>(mapped, -32767, 32767);
+ };
+
+ for (int i = 0; i < joystick->naxes; i++)
+ SDL_PrivateJoystickAxis(joystick, i, map_joystick_value(state.axis[i]));
+ SDL_PrivateJoystickAxis(joystick, i, state.axis[i]);
+
+ for (int i = 0; i < joystick->nbuttons; i++)
+ SDL_PrivateJoystickButton(joystick, i, state.buttons[i]);
@ -556,9 +545,9 @@ diff -ruN SDL2-2.32.8/src/joystick/banan_os/SDL_banan_os_joystick.cpp SDL2-2.32.
+
+static SDL_bool BANANOS_JoystickGetGamepadMapping(int device_index, SDL_GamepadMapping* out)
+{
+ (void)device_index;
+ using namespace LibInput;
+
+ // FIXME: this is hardcoded to a PS3 controller layout :D
+ (void)device_index;
+
+ memset(out, 0, sizeof(SDL_GamepadMapping));
+
@ -570,34 +559,33 @@ diff -ruN SDL2-2.32.8/src/joystick/banan_os/SDL_banan_os_joystick.cpp SDL2-2.32.
+ out->name.kind = EMappingKind_Axis; \
+ out->name.target = axis
+
+ BANANOS_MAP_BUTTON(a, 14);
+ BANANOS_MAP_BUTTON(b, 13);
+ BANANOS_MAP_BUTTON(x, 15);
+ BANANOS_MAP_BUTTON(y, 12);
+ BANANOS_MAP_BUTTON(a, JSB_FACE_DOWN);
+ BANANOS_MAP_BUTTON(b, JSB_FACE_RIGHT);
+ BANANOS_MAP_BUTTON(x, JSB_FACE_LEFT);
+ BANANOS_MAP_BUTTON(y, JSB_FACE_UP);
+
+ BANANOS_MAP_BUTTON(back, 0);
+ BANANOS_MAP_BUTTON(guide, 16);
+ BANANOS_MAP_BUTTON(start, 3);
+ BANANOS_MAP_BUTTON(back, JSB_SELECT);
+ BANANOS_MAP_BUTTON(guide, JSB_MENU);
+ BANANOS_MAP_BUTTON(start, JSB_START);
+
+ BANANOS_MAP_BUTTON(leftstick, 1);
+ BANANOS_MAP_BUTTON(rightstick, 2);
+ BANANOS_MAP_BUTTON(leftstick, JSB_STICK_LEFT);
+ BANANOS_MAP_BUTTON(rightstick, JSB_STICK_RIGHT);
+
+ BANANOS_MAP_BUTTON(leftshoulder, 10);
+ BANANOS_MAP_BUTTON(rightshoulder, 11);
+ BANANOS_MAP_BUTTON(leftshoulder, JSB_SHOULDER_LEFT);
+ BANANOS_MAP_BUTTON(rightshoulder, JSB_SHOULDER_RIGHT);
+
+ BANANOS_MAP_BUTTON(dpup, 4);
+ BANANOS_MAP_BUTTON(dpdown, 6);
+ BANANOS_MAP_BUTTON(dpleft, 7);
+ BANANOS_MAP_BUTTON(dpright, 5);
+ BANANOS_MAP_BUTTON(dpup, JSB_DPAD_UP);
+ BANANOS_MAP_BUTTON(dpdown, JSB_DPAD_DOWN);
+ BANANOS_MAP_BUTTON(dpleft, JSB_DPAD_LEFT);
+ BANANOS_MAP_BUTTON(dpright, JSB_DPAD_RIGHT);
+
+ BANANOS_MAP_AXIS(leftx, 0);
+ BANANOS_MAP_AXIS(lefty, 1);
+ BANANOS_MAP_AXIS(rightx, 2);
+ BANANOS_MAP_AXIS(righty, 3);
+ BANANOS_MAP_AXIS(leftx, JSA_STICK_LEFT_X);
+ BANANOS_MAP_AXIS(lefty, JSA_STICK_LEFT_Y);
+ BANANOS_MAP_AXIS(rightx, JSA_STICK_RIGHT_X);
+ BANANOS_MAP_AXIS(righty, JSA_STICK_RIGHT_Y);
+
+ // TODO: these should probably be axis
+ BANANOS_MAP_BUTTON(lefttrigger, 8);
+ BANANOS_MAP_BUTTON(righttrigger, 9);
+ BANANOS_MAP_AXIS(lefttrigger, JSA_TRIGGER_LEFT);
+ BANANOS_MAP_AXIS(righttrigger, JSA_TRIGGER_RIGHT);
+
+#undef BANANOS_MAP_BUTTON
+#undef BANANOS_MAP_AXIS
@ -667,7 +655,7 @@ diff -ruN SDL2-2.32.8/src/joystick/banan_os/SDL_banan_os_joystick.cpp SDL2-2.32.
+/* vi: set ts=4 sw=4 expandtab: */
diff -ruN SDL2-2.32.8/src/joystick/SDL_joystick.c SDL2-2.32.8-banan_os/src/joystick/SDL_joystick.c
--- SDL2-2.32.8/src/joystick/SDL_joystick.c 2025-01-01 17:47:53.000000000 +0200
+++ SDL2-2.32.8-banan_os/src/joystick/SDL_joystick.c 2026-01-03 19:52:38.740921854 +0200
+++ SDL2-2.32.8-banan_os/src/joystick/SDL_joystick.c 2026-01-07 19:04:34.373890653 +0200
@@ -85,6 +85,9 @@
#ifdef SDL_JOYSTICK_HAIKU
&SDL_HAIKU_JoystickDriver,
@ -680,7 +668,7 @@ diff -ruN SDL2-2.32.8/src/joystick/SDL_joystick.c SDL2-2.32.8-banan_os/src/joyst
#endif
diff -ruN SDL2-2.32.8/src/joystick/SDL_sysjoystick.h SDL2-2.32.8-banan_os/src/joystick/SDL_sysjoystick.h
--- SDL2-2.32.8/src/joystick/SDL_sysjoystick.h 2025-01-01 17:47:53.000000000 +0200
+++ SDL2-2.32.8-banan_os/src/joystick/SDL_sysjoystick.h 2026-01-03 19:52:50.906765671 +0200
+++ SDL2-2.32.8-banan_os/src/joystick/SDL_sysjoystick.h 2026-01-07 19:04:34.374337431 +0200
@@ -235,6 +235,7 @@
/* The available joystick drivers */
@ -691,7 +679,7 @@ diff -ruN SDL2-2.32.8/src/joystick/SDL_sysjoystick.h SDL2-2.32.8-banan_os/src/jo
extern SDL_JoystickDriver SDL_DUMMY_JoystickDriver;
diff -ruN SDL2-2.32.8/src/misc/banan_os/SDL_sysurl.cpp SDL2-2.32.8-banan_os/src/misc/banan_os/SDL_sysurl.cpp
--- SDL2-2.32.8/src/misc/banan_os/SDL_sysurl.cpp 1970-01-01 02:00:00.000000000 +0200
+++ SDL2-2.32.8-banan_os/src/misc/banan_os/SDL_sysurl.cpp 2026-01-03 19:50:45.780251300 +0200
+++ SDL2-2.32.8-banan_os/src/misc/banan_os/SDL_sysurl.cpp 2026-01-07 19:04:34.379748697 +0200
@@ -0,0 +1,30 @@
+/*
+ Simple DirectMedia Layer
@ -725,7 +713,7 @@ diff -ruN SDL2-2.32.8/src/misc/banan_os/SDL_sysurl.cpp SDL2-2.32.8-banan_os/src/
+
diff -ruN SDL2-2.32.8/src/video/banan_os/SDL_banan_os_clipboard.cpp SDL2-2.32.8-banan_os/src/video/banan_os/SDL_banan_os_clipboard.cpp
--- SDL2-2.32.8/src/video/banan_os/SDL_banan_os_clipboard.cpp 1970-01-01 02:00:00.000000000 +0200
+++ SDL2-2.32.8-banan_os/src/video/banan_os/SDL_banan_os_clipboard.cpp 2026-01-03 19:50:45.780447206 +0200
+++ SDL2-2.32.8-banan_os/src/video/banan_os/SDL_banan_os_clipboard.cpp 2026-01-07 19:04:34.379995308 +0200
@@ -0,0 +1,51 @@
+/*
+ Simple DirectMedia Layer
@ -780,7 +768,7 @@ diff -ruN SDL2-2.32.8/src/video/banan_os/SDL_banan_os_clipboard.cpp SDL2-2.32.8-
+/* vi: set ts=4 sw=4 expandtab: */
diff -ruN SDL2-2.32.8/src/video/banan_os/SDL_banan_os_clipboard.h SDL2-2.32.8-banan_os/src/video/banan_os/SDL_banan_os_clipboard.h
--- SDL2-2.32.8/src/video/banan_os/SDL_banan_os_clipboard.h 1970-01-01 02:00:00.000000000 +0200
+++ SDL2-2.32.8-banan_os/src/video/banan_os/SDL_banan_os_clipboard.h 2026-01-03 19:50:45.780567334 +0200
+++ SDL2-2.32.8-banan_os/src/video/banan_os/SDL_banan_os_clipboard.h 2026-01-07 19:04:34.380137576 +0200
@@ -0,0 +1,43 @@
+/*
+ Simple DirectMedia Layer
@ -827,7 +815,7 @@ diff -ruN SDL2-2.32.8/src/video/banan_os/SDL_banan_os_clipboard.h SDL2-2.32.8-ba
+/* vi: set ts=4 sw=4 expandtab: */
diff -ruN SDL2-2.32.8/src/video/banan_os/SDL_banan_os_message_box.cpp SDL2-2.32.8-banan_os/src/video/banan_os/SDL_banan_os_message_box.cpp
--- SDL2-2.32.8/src/video/banan_os/SDL_banan_os_message_box.cpp 1970-01-01 02:00:00.000000000 +0200
+++ SDL2-2.32.8-banan_os/src/video/banan_os/SDL_banan_os_message_box.cpp 2026-01-03 19:50:45.780690325 +0200
+++ SDL2-2.32.8-banan_os/src/video/banan_os/SDL_banan_os_message_box.cpp 2026-01-07 19:04:34.380308339 +0200
@@ -0,0 +1,60 @@
+/*
+ Simple DirectMedia Layer
@ -891,7 +879,7 @@ diff -ruN SDL2-2.32.8/src/video/banan_os/SDL_banan_os_message_box.cpp SDL2-2.32.
+/* vi: set ts=4 sw=4 expandtab: */
diff -ruN SDL2-2.32.8/src/video/banan_os/SDL_banan_os_message_box.h SDL2-2.32.8-banan_os/src/video/banan_os/SDL_banan_os_message_box.h
--- SDL2-2.32.8/src/video/banan_os/SDL_banan_os_message_box.h 1970-01-01 02:00:00.000000000 +0200
+++ SDL2-2.32.8-banan_os/src/video/banan_os/SDL_banan_os_message_box.h 2026-01-03 19:50:45.780815551 +0200
+++ SDL2-2.32.8-banan_os/src/video/banan_os/SDL_banan_os_message_box.h 2026-01-07 19:04:34.380550899 +0200
@@ -0,0 +1,45 @@
+/*
+ Simple DirectMedia Layer
@ -940,7 +928,7 @@ diff -ruN SDL2-2.32.8/src/video/banan_os/SDL_banan_os_message_box.h SDL2-2.32.8-
+/* vi: set ts=4 sw=4 expandtab: */
diff -ruN SDL2-2.32.8/src/video/banan_os/SDL_banan_os_video.cpp SDL2-2.32.8-banan_os/src/video/banan_os/SDL_banan_os_video.cpp
--- SDL2-2.32.8/src/video/banan_os/SDL_banan_os_video.cpp 1970-01-01 02:00:00.000000000 +0200
+++ SDL2-2.32.8-banan_os/src/video/banan_os/SDL_banan_os_video.cpp 2026-01-03 19:50:45.780934771 +0200
+++ SDL2-2.32.8-banan_os/src/video/banan_os/SDL_banan_os_video.cpp 2026-01-07 19:04:34.380720824 +0200
@@ -0,0 +1,729 @@
+/*
+ Simple DirectMedia Layer
@ -1673,7 +1661,7 @@ diff -ruN SDL2-2.32.8/src/video/banan_os/SDL_banan_os_video.cpp SDL2-2.32.8-bana
+/* vi: set ts=4 sw=4 expandtab: */
diff -ruN SDL2-2.32.8/src/video/SDL_sysvideo.h SDL2-2.32.8-banan_os/src/video/SDL_sysvideo.h
--- SDL2-2.32.8/src/video/SDL_sysvideo.h 2025-05-20 00:24:41.000000000 +0300
+++ SDL2-2.32.8-banan_os/src/video/SDL_sysvideo.h 2026-01-03 19:50:45.781358361 +0200
+++ SDL2-2.32.8-banan_os/src/video/SDL_sysvideo.h 2026-01-07 19:04:34.381316574 +0200
@@ -462,6 +462,7 @@
extern VideoBootStrap WINDOWS_bootstrap;
extern VideoBootStrap WINRT_bootstrap;
@ -1684,7 +1672,7 @@ diff -ruN SDL2-2.32.8/src/video/SDL_sysvideo.h SDL2-2.32.8-banan_os/src/video/SD
extern VideoBootStrap Android_bootstrap;
diff -ruN SDL2-2.32.8/src/video/SDL_video.c SDL2-2.32.8-banan_os/src/video/SDL_video.c
--- SDL2-2.32.8/src/video/SDL_video.c 2025-05-20 00:24:41.000000000 +0300
+++ SDL2-2.32.8-banan_os/src/video/SDL_video.c 2026-01-03 19:50:45.782037083 +0200
+++ SDL2-2.32.8-banan_os/src/video/SDL_video.c 2026-01-07 19:04:34.398132645 +0200
@@ -96,6 +96,9 @@
#ifdef SDL_VIDEO_DRIVER_HAIKU
&HAIKU_bootstrap,

View File

@ -54,9 +54,14 @@ struct winsize
#define SND_GET_SAMPLE_RATE 61 /* stores sample rate to uint32_t argument */
#define SND_RESET_BUFFER 62 /* stores the size of internal buffer to uint32_t argument and clears the buffer */
#define SND_GET_BUFFERSZ 63 /* stores the size of internal buffer to uint32_t argument */
#define SND_GET_TOTAL_PINS 64 /* gets the number of pins on the current device */
#define SND_GET_PIN 65 /* gets the currently active pin */
#define SND_SET_PIN 66 /* sets the currently active pin */
#define SND_GET_TOTAL_PINS 64 /* gets the number of pins on the current device as uint32_t */
#define SND_GET_PIN 65 /* gets the currently active pin as uint32_t */
#define SND_SET_PIN 66 /* sets the currently active pin to uint32_t */
#define JOYSTICK_GET_LEDS 80 /* get controller led bitmap to uint8_t argument */
#define JOYSTICK_SET_LEDS 81 /* set controller leds to uint8_t bitmap */
#define JOYSTICK_GET_RUMBLE 82 /* get controller rumble strength to uint8_t argument */
#define JOYSTICK_SET_RUMBLE 83 /* set controller rumble strength to uint8_t argument */
int ioctl(int, int, ...);

View File

@ -5,7 +5,45 @@
namespace LibInput
{
// TODO: not used but here if we ever make controller
enum JoystickButton
{
JSB_DPAD_UP,
JSB_DPAD_DOWN,
JSB_DPAD_LEFT,
JSB_DPAD_RIGHT,
JSB_FACE_UP,
JSB_FACE_DOWN,
JSB_FACE_LEFT,
JSB_FACE_RIGHT,
JSB_STICK_LEFT,
JSB_STICK_RIGHT,
JSB_SHOULDER_LEFT,
JSB_SHOULDER_RIGHT,
JSB_MENU,
JSB_START,
JSB_SELECT,
JSB_COUNT,
};
enum JoystickAxis
{
JSA_STICK_LEFT_X,
JSA_STICK_LEFT_Y,
JSA_STICK_RIGHT_X,
JSA_STICK_RIGHT_Y,
JSA_TRIGGER_LEFT,
JSA_TRIGGER_RIGHT,
JSA_COUNT,
};
// TODO: not used but exists if we ever make controller
// support generating events instead of being polled
struct JoystickEvent
{
@ -14,15 +52,9 @@ namespace LibInput
struct JoystickState
{
struct Axis
{
int64_t value;
int64_t min;
int64_t max;
};
Axis axis[4];
bool buttons[32];
// axis are mapped to range [-32767, +32767]
int16_t axis[JSA_COUNT];
bool buttons[JSB_COUNT];
};
}

View File

@ -4,6 +4,7 @@
#include <stdlib.h>
#include <string.h>
#include <sys/framebuffer.h>
#include <sys/ioctl.h>
#include <sys/mman.h>
#include <termios.h>
@ -47,13 +48,6 @@ void cleanup()
tcsetattr(STDIN_FILENO, TCSANOW, &original_termios);
}
int map_joystick(const LibInput::JoystickState::Axis& axis, float min, float max)
{
if (axis.min == axis.max)
return (min + max) / 2;
return (axis.value - axis.min) * (max - min) / (axis.max - axis.min) + min;
}
int main(int argc, char** argv)
{
const char* fb_path = "/dev/fb0";
@ -132,6 +126,12 @@ int main(int argc, char** argv)
return 1;
}
uint8_t led_bitmap { 0b0001 };
ioctl(joystick_fd, JOYSTICK_SET_LEDS, &led_bitmap);
bool old_lshoulder { false }, old_rshoulder { false };
uint8_t rumble_strength { 0x00 };
uint32_t color = 0xFF0000;
int circle_x = fb_info.width / 2;
int circle_y = fb_info.height / 2;
@ -142,9 +142,20 @@ int main(int argc, char** argv)
draw_circle(circle_x, circle_y, radius, color);
msync(fb_mmap, fb_bytes, MS_SYNC);
timespec last_led_update;
clock_gettime(CLOCK_MONOTONIC, &last_led_update);
timespec prev_frame_ts;
clock_gettime(CLOCK_MONOTONIC, &prev_frame_ts);
while (true)
{
LibInput::JoystickState state {};
using namespace LibInput;
timespec current_ts;
clock_gettime(CLOCK_MONOTONIC, &current_ts);
JoystickState state {};
if (read(joystick_fd, &state, sizeof(state)) == -1)
{
fprintf(stderr, "read: ");
@ -152,9 +163,9 @@ int main(int argc, char** argv)
return 1;
}
const int dx = map_joystick(state.axis[0], -50, 50);
const int dy = map_joystick(state.axis[1], -50, 50);
const int dr = map_joystick(state.axis[3], 5, -5);
const int dx = state.axis[JSA_STICK_LEFT_X] / 655;
const int dy = state.axis[JSA_STICK_LEFT_Y] / 655;
const int dr = state.axis[JSA_STICK_RIGHT_Y] / 6553;
draw_circle(circle_x, circle_y, radius, 0x000000);
@ -164,21 +175,59 @@ int main(int argc, char** argv)
circle_y = BAN::Math::clamp<int>(circle_y + dy, 0, fb_info.height);
radius = BAN::Math::clamp<int>(radius + dr, 1, 100);
if (state.buttons[12])
if (state.buttons[JSB_FACE_UP])
color = 0xFF0000;
if (state.buttons[13])
if (state.buttons[JSB_FACE_DOWN])
color = 0x00FF00;
if (state.buttons[14])
if (state.buttons[JSB_FACE_LEFT])
color = 0x0000FF;
if (state.buttons[15])
if (state.buttons[JSB_FACE_RIGHT])
color = 0xFFFFFF;
if ((state.buttons[JSB_SHOULDER_LEFT] && !old_lshoulder) != (state.buttons[JSB_SHOULDER_RIGHT] && !old_rshoulder))
{
if (state.buttons[JSB_SHOULDER_LEFT] && !old_lshoulder)
led_bitmap = (led_bitmap - 1) & 0x0F;
if (state.buttons[JSB_SHOULDER_RIGHT] && !old_rshoulder)
led_bitmap = (led_bitmap + 1) & 0x0F;
ioctl(joystick_fd, JOYSTICK_SET_LEDS, &led_bitmap);
}
old_lshoulder = state.buttons[JSB_SHOULDER_LEFT];
old_rshoulder = state.buttons[JSB_SHOULDER_RIGHT];
if ((state.axis[JSA_TRIGGER_LEFT] > 0) != (state.axis[JSA_TRIGGER_RIGHT] > 0))
{
const auto old_rumble = rumble_strength;
if (state.axis[JSA_TRIGGER_LEFT] > 0)
rumble_strength = (rumble_strength <= 0x05) ? 0 : rumble_strength - 5;
if (state.axis[JSA_TRIGGER_RIGHT] > 0)
rumble_strength = (rumble_strength >= 0xFA) ? 0 : rumble_strength + 5;
if (rumble_strength != old_rumble)
ioctl(joystick_fd, JOYSTICK_SET_RUMBLE, &rumble_strength);
}
draw_circle(circle_x, circle_y, radius, color);
msync(fb_mmap, fb_bytes, MS_SYNC);
usleep(16666);
const uint64_t current_us =
current_ts.tv_sec * 1'000'000 +
current_ts.tv_nsec / 1000;
const uint64_t last_frame_us =
prev_frame_ts.tv_sec * 1'000'000 +
prev_frame_ts.tv_nsec / 1000;
msync(fb_mmap, fb_bytes, MS_SYNC);
const uint64_t wakeup_us = last_frame_us + 16'666;
if (current_us < wakeup_us)
{
const uint32_t sleep_us = wakeup_us - current_us;
const timespec sleep_ts {
.tv_sec = 0,
.tv_nsec = static_cast<long>(wakeup_us - current_us) * 1000,
};
nanosleep(&sleep_ts, nullptr);
}
prev_frame_ts = current_ts;
}
}